@eventmodelers/cli 1.0.90 → 1.0.91
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/README.md +2 -0
- package/cli.js +26 -2
- package/lib/update-check.js +67 -0
- package/package.json +4 -1
- package/shared/build-kit/lib/adapters/pocketbase-realtime-adapter.js +11 -3
- package/shared/build-kit/lib/adapters/realtime-adapter.js +3 -1
- package/shared/build-kit/lib/adapters/supabase-realtime-adapter.js +14 -3
- package/shared/build-kit/lib/ralph.js +136 -59
- package/shared/build-kit/lib/redact.js +36 -0
- package/shared/build-kit/lib/turn.js +56 -20
- package/shared/build-kit/ralph-claude.js +7 -19
- package/shared/build-kit/ralph-exec.js +5 -9
- package/shared/build-kit/realtime-agent.js +4 -2
- package/shared/skills/connect/SKILL.md +14 -9
- package/shared/skills/learn-eventmodelers-api/SKILL.md +60 -21
- package/shared/skills/load-slice/SKILL.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +18 -9
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +31 -11
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +16 -13
- package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +6 -6
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +8 -12
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +16 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +3 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +1 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +3 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +3 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +1 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +3 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +12 -4
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +7 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +4 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +22 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +12 -3
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +18 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +12 -3
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +20 -14
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +12 -12
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +6 -4
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +10 -10
- package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +4 -4
- package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +11 -24
- package/stacks/modeling-kit/templates/kit/AGENTS.md +3 -2
- package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +6 -4
- package/stacks/modeling-kit/templates/kit/CLAUDE.md +9 -4
- package/stacks/react/templates/build-kit/lib/backend-prompt.md +106 -102
- package/stacks/react/templates/build-kit/lib/prompt.md +102 -106
|
@@ -45,30 +45,37 @@ Save the full slice list. Count total slices and group by status:
|
|
|
45
45
|
|
|
46
46
|
## Step 3 — Discover contexts
|
|
47
47
|
|
|
48
|
-
Fetch
|
|
48
|
+
Fetch the `MODEL_CONTEXT` nodes **and** the timelines — a timeline with no assigned or connected context is its own context (named after the timeline), so the MODEL_CONTEXT nodes alone miss it. Only ids and titles are needed, so use the `line` projection:
|
|
49
49
|
|
|
50
50
|
**Prefer MCP:**
|
|
51
51
|
```
|
|
52
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT" }
|
|
52
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT", "projection": "line" }
|
|
53
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "CHAPTER", "projection": "line" }
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 3 — Discover contexts".
|
|
56
57
|
|
|
57
58
|
- If a `contextName` argument was given, filter to that single context and skip others.
|
|
58
|
-
-
|
|
59
|
-
-
|
|
59
|
+
- Record each MODEL_CONTEXT's `id` and `title`, and each timeline's `id` and `title`.
|
|
60
|
+
- Step 4 fetches every MODEL_CONTEXT by title first. Each slice in a response carries its `chapter`; every timeline never named there is its own context (or holds no slices) — fetch it too, passing the timeline's title as `contextName`. With no MODEL_CONTEXT nodes at all, that is every timeline.
|
|
60
61
|
|
|
61
62
|
---
|
|
62
63
|
|
|
63
64
|
## Step 4 — Fetch slice data per context
|
|
64
65
|
|
|
65
|
-
For each resolved context, fetch the full
|
|
66
|
+
For each resolved context, fetch two trimmed projections instead of the full graph — this analysis never needs comments, codegen prompts, notes or screen images:
|
|
66
67
|
|
|
67
68
|
**Prefer MCP:**
|
|
68
69
|
```
|
|
69
|
-
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>" }
|
|
70
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>", "projection": "fields", "format": "toon" }
|
|
71
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>", "projection": "specs", "format": "toon" }
|
|
70
72
|
```
|
|
71
73
|
|
|
74
|
+
- `fields` — slice header (id, title, status, sliceType, chapter, context) plus every element with its fields and **dependencies**. Covers 5a (inventory), 5b (status), 5d (gaps) and 5f (structural shapes — the bed needs the SCREEN → COMMAND dependencies).
|
|
75
|
+
- `specs` — slice header plus specifications (given/when/then) and storylines, no elements. Covers 5c (spec coverage).
|
|
76
|
+
- If the user only asked for counts/status/coverage (no gap or shape check), use `"projection": "outline"` instead of `fields` — elements as `{id, title, type}` only, the cheapest read.
|
|
77
|
+
- `outline` and `specs` only encode as `json`, `yaml` or `toon` (`textual`/`emlang`/`esdm` return `PROJECTION_FORMAT_UNSUPPORTED`); use `toon` (token-efficient) or `json`.
|
|
78
|
+
|
|
72
79
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 4 — Fetch slice data per context".
|
|
73
80
|
|
|
74
81
|
Fetch all contexts in parallel if there are multiple. Merge results, keyed by context name.
|
|
@@ -97,7 +104,7 @@ Group slices by status. Report counts per status. Flag any status that suggests
|
|
|
97
104
|
|
|
98
105
|
### 5c — Spec coverage
|
|
99
106
|
|
|
100
|
-
For each slice
|
|
107
|
+
For each slice in the `specs` projection, check whether its specifications are non-empty. Calculate:
|
|
101
108
|
- Slices **with** at least one GWT scenario
|
|
102
109
|
- Slices **without** any scenarios (grouped by slice type if detectable)
|
|
103
110
|
|
|
@@ -201,8 +208,10 @@ If a context was specified but not found, tell the user clearly and list the con
|
|
|
201
208
|
**Prefer MCP:**
|
|
202
209
|
```
|
|
203
210
|
mcp__eventmodelers__list_slices { "boardId": "$BOARD_ID" }
|
|
204
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT" }
|
|
205
|
-
|
|
211
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "MODEL_CONTEXT", "projection": "line" }
|
|
212
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "CHAPTER", "projection": "line" }
|
|
213
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "Ordering", "projection": "fields", "format": "toon" }
|
|
214
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "Ordering", "projection": "specs", "format": "toon" }
|
|
206
215
|
```
|
|
207
216
|
|
|
208
217
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Example — full board analysis".
|
|
@@ -19,23 +19,35 @@ Response: `{ "slices": [{ "id": "<uuid>", "title": "<name>", "status": "<status>
|
|
|
19
19
|
curl -s \
|
|
20
20
|
-H "x-token: $TOKEN" \
|
|
21
21
|
-H "x-user-id: analyze-existing-model" \
|
|
22
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=MODEL_CONTEXT"
|
|
22
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=MODEL_CONTEXT&projection=line"
|
|
23
|
+
|
|
24
|
+
# Timelines too — one without a context is its own context, named after the timeline
|
|
25
|
+
curl -s \
|
|
26
|
+
-H "x-token: $TOKEN" \
|
|
27
|
+
-H "x-user-id: analyze-existing-model" \
|
|
28
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line"
|
|
23
29
|
```
|
|
24
30
|
|
|
31
|
+
Returns `{id, type, title}` per node — all this step needs.
|
|
32
|
+
|
|
25
33
|
## Step 4 — Fetch slice data per context
|
|
26
34
|
|
|
27
35
|
```bash
|
|
28
36
|
curl -s \
|
|
29
37
|
-H "x-token: $TOKEN" \
|
|
30
38
|
-H "x-user-id: analyze-existing-model" \
|
|
31
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME
|
|
39
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=fields&format=toon"
|
|
40
|
+
|
|
41
|
+
curl -s \
|
|
42
|
+
-H "x-token: $TOKEN" \
|
|
43
|
+
-H "x-user-id: analyze-existing-model" \
|
|
44
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=specs&format=toon"
|
|
32
45
|
```
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
- `
|
|
36
|
-
- `elements
|
|
37
|
-
- `
|
|
38
|
-
- `edges`: relationships between elements
|
|
47
|
+
- `projection=fields` — slice header (id, title, status, sliceType, chapter, context) + elements (commands, events, readmodels, screens, processors, tables) with their fields and dependencies. No specs/comments.
|
|
48
|
+
- `projection=specs` — slice header + specifications (given/when/then) and storylines. No elements.
|
|
49
|
+
- `projection=outline` — slice header + elements as `{id, title, type}` only; use it instead of `fields` when no gap/shape check is needed.
|
|
50
|
+
- `outline` and `specs` require `format=json|yaml|toon` (others → 400 `PROJECTION_FORMAT_UNSUPPORTED`).
|
|
39
51
|
|
|
40
52
|
## Example — full board analysis
|
|
41
53
|
|
|
@@ -46,17 +58,25 @@ curl -s \
|
|
|
46
58
|
-H "x-user-id: analyze-existing-model" \
|
|
47
59
|
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata/slices"
|
|
48
60
|
|
|
49
|
-
# 2. Fetch MODEL_CONTEXT nodes
|
|
61
|
+
# 2. Fetch MODEL_CONTEXT nodes and timelines (a timeline without a context is its own context)
|
|
62
|
+
curl -s \
|
|
63
|
+
-H "x-token: $TOKEN" \
|
|
64
|
+
-H "x-user-id: analyze-existing-model" \
|
|
65
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=MODEL_CONTEXT&projection=line"
|
|
50
66
|
curl -s \
|
|
51
67
|
-H "x-token: $TOKEN" \
|
|
52
68
|
-H "x-user-id: analyze-existing-model" \
|
|
53
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=
|
|
69
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line"
|
|
54
70
|
|
|
55
|
-
# 3. Fetch
|
|
71
|
+
# 3. Fetch fields + specs projections for a context
|
|
72
|
+
curl -s \
|
|
73
|
+
-H "x-token: $TOKEN" \
|
|
74
|
+
-H "x-user-id: analyze-existing-model" \
|
|
75
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=Ordering&projection=fields&format=toon"
|
|
56
76
|
curl -s \
|
|
57
77
|
-H "x-token: $TOKEN" \
|
|
58
78
|
-H "x-user-id: analyze-existing-model" \
|
|
59
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=Ordering"
|
|
79
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=Ordering&projection=specs&format=toon"
|
|
60
80
|
```
|
|
61
81
|
|
|
62
82
|
Replace `$TOKEN`, `$ORG_ID`, `$BOARD_ID`, and the context name with real values resolved from the `connect` skill.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: attributes
|
|
3
|
-
description: Add a new attribute or rename an existing attribute across a chain of elements from a source
|
|
3
|
+
description: Add a new attribute or rename an existing attribute across a chain of elements from a source element to a target element, following inbound dependencies
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Attributes
|
|
@@ -9,7 +9,7 @@ description: Add a new attribute or rename an existing attribute across a chain
|
|
|
9
9
|
|
|
10
10
|
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
11
11
|
|
|
12
|
-
You are propagating an attribute change (add or rename) across a chain of elements on an eventmodelers board. You start at the target
|
|
12
|
+
You are propagating an attribute change (add or rename) across a chain of elements on an eventmodelers board. You start at the target element, apply the change, then walk backwards through inbound dependencies until you reach the source element, applying the change to every element along the way.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -17,8 +17,8 @@ You are propagating an attribute change (add or rename) across a chain of elemen
|
|
|
17
17
|
|
|
18
18
|
Ask the user for all required information **in a single message** (do not ask one at a time):
|
|
19
19
|
|
|
20
|
-
1. **Target
|
|
21
|
-
2. **Source
|
|
20
|
+
1. **Target element** — the end of the chain: a node id, a title, or a cell name (e.g. `B2`)
|
|
21
|
+
2. **Source element** — the start of the chain: a node id, a title, or a cell name (e.g. `A2`)
|
|
22
22
|
3. **Operation** — `add` a new attribute, or `rename` an existing one
|
|
23
23
|
4. If **rename**: which attribute name to rename FROM, and what to rename it TO
|
|
24
24
|
5. If **add**: the name of the new attribute to add
|
|
@@ -29,24 +29,27 @@ If any of these were already provided in `$ARGUMENTS`, skip asking for them.
|
|
|
29
29
|
|
|
30
30
|
## Step 2 — Resolve the chain
|
|
31
31
|
|
|
32
|
-
**Prefer MCP:** `get_attribute_chain` resolves every node between the source and target
|
|
32
|
+
**Prefer MCP:** `get_attribute_chain` resolves every node in the columns between the source and target nodes (inclusive), ordered target→source (top-to-bottom within a column), each as `{id, type, title, cellName, fields}` — this collapses the manual inbound-edge walk below into one call. You still need `TIMELINE_ID` (the chapter to search): if multiple chapters exist on the board, resolve which one first (see 2a fallback below, or `mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "CHAPTER", "projection": "line" }` — ids/titles are enough to pick one) and ask the user if ambiguous.
|
|
33
|
+
|
|
34
|
+
First resolve target and source to node ids (`TARGET_NODE_ID`, `SOURCE_NODE_ID`):
|
|
35
|
+
- **Node id** (incl. a node the task or a comment points at) — use it as-is.
|
|
36
|
+
- **Title** — `mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "chapterId": "$CHAPTER_ID", "name": "<title>", "projection": "line" }`; ask the user if several match.
|
|
37
|
+
- **Cell name** (e.g. `B2`) — `mcp__eventmodelers__get_board_outline { "boardId": "$BOARD_ID", "chapterId": "$CHAPTER_ID" }` and take the node whose `cellName` equals it. `get_board_outline` is the only read that reports `cellName`; never compute a node from a cell address yourself.
|
|
33
38
|
|
|
34
39
|
```
|
|
35
40
|
mcp__eventmodelers__get_attribute_chain {
|
|
36
41
|
"boardId": "$BOARD_ID",
|
|
37
42
|
"timelineId": "$CHAPTER_ID",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
43
|
+
"targetNodeId": "<TARGET_NODE_ID>",
|
|
44
|
+
"sourceNodeId": "<SOURCE_NODE_ID>"
|
|
40
45
|
}
|
|
41
46
|
```
|
|
42
47
|
|
|
43
|
-
The result gives you the ordered chain directly — save it as the chain used in Step 4, and skip the manual walk in 2a–3c below. Continue with the fallback only if MCP isn't connected.
|
|
44
|
-
|
|
45
|
-
**Both cell names must be real.** When the task named a node (an id, a title, a comment on it) instead of a cell, read that node's `cellName` off `get_board_outline` (every node in the chapter, with its address), `get_nodes`, or `get_node` — all three report it. Never compute an address from where a node appeared in a list, and never hand `get_attribute_chain` an address you have not read: a cell that exists but holds a different node resolves a wrong chain without any error.
|
|
48
|
+
The result gives you the ordered chain directly — save it as the chain used in Step 4, and skip the manual walk in 2a–3c below. A `NODE_NOT_IN_TIMELINE` error means one of the two nodes isn't placed in `$CHAPTER_ID` — pick the right chapter (or tell the user) rather than retrying. Continue with the fallback only if MCP isn't connected.
|
|
46
49
|
|
|
47
|
-
### Fallback (no MCP) — resolve
|
|
50
|
+
### Fallback (no MCP) — resolve target and source to nodes
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
A node id needs no resolving (`GET .../nodes/:nodeId`); a title resolves via `GET .../nodes?chapterId=&name=`. For a cell name, resolve it to a node using the exact same cell-resolution steps as the `examples` skill's "2c — Cell name" section (fetch chapters, fetch the chapter fresh to decode the grid, decode the cell name into a `CELL_ID`, then always fetch the cell live via REST `?cellId=&timelineId=` — MCP `get_nodes` has no `cellId` filter) — see there for the full mechanics, substituting `x-user-id: attributes-skill`.
|
|
50
53
|
|
|
51
54
|
Take the first non-CHAPTER result as the node for each cell. Save as `TARGET_NODE` and `SOURCE_NODE`.
|
|
52
55
|
|
|
@@ -165,7 +168,7 @@ Nodes that are skipped (field already exists / field not found) simply contribut
|
|
|
165
168
|
|
|
166
169
|
Verify the response is HTTP 200. If the batch fails, report the error and stop; nothing was partially applied from your side, so re-run after fixing the cause rather than retrying node by node.
|
|
167
170
|
|
|
168
|
-
If you also need to verify the result, re-read the whole chain in one call — `get_nodes { "boardId": "$BOARD_ID", "nodeIds": [<every node id you just wrote>] }` — never one `get_node` per node.
|
|
171
|
+
If you also need to verify the result, re-read the whole chain in one call — `get_nodes { "boardId": "$BOARD_ID", "nodeIds": [<every node id you just wrote>], "projection": "line" }` when you only check that the attribute name is present/renamed (`line` lists each node's field names), or without `projection` when you must also check the field's type/example — never one `get_node` per node.
|
|
169
172
|
|
|
170
173
|
---
|
|
171
174
|
|
|
@@ -5,10 +5,12 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## 3a — Use Node Edges
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$EDGE_SOURCE_ID" \
|
|
8
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$EDGE_SOURCE_ID?projection=edges" \
|
|
9
9
|
-H "x-token: $TOKEN" -H "x-user-id: attributes-skill"
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
`projection=edges` returns `{nodeId, edges}` — the node's inbound/outbound connections only, no content (the full node record carries no edges). Fields for Step 4 come from the chapter-scoped `GET .../nodes?chapterId=` read.
|
|
13
|
+
|
|
12
14
|
## Step 4 — Apply the Change to Each Node in the Chain
|
|
13
15
|
|
|
14
16
|
Build the payload with Python to avoid JSON escaping issues, then POST it:
|
|
@@ -256,11 +256,11 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>"
|
|
|
256
256
|
|
|
257
257
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 6 — Fetch chapter grid".
|
|
258
258
|
|
|
259
|
-
From the result (`rows`/`columns`/`cells` directly
|
|
259
|
+
From the result (`rows`/`columns`/`cells` directly — the REST fallback's `?projection=cells` returns the same shape):
|
|
260
260
|
- `rows` — find the row with `type === "actor"` → save its `id` as `actorRowId` and its 0-based position in `rows` as `actorRowIndex`
|
|
261
261
|
- `columns` — ordered list; build an empty-column queue, remembering each entry's 0-based position in `columns` as its `columnIndex`
|
|
262
262
|
|
|
263
|
-
**Cell ID convention**: `<rowId>-<columnId>` — always computed directly, never looked up. **Cell name
|
|
263
|
+
**Cell ID convention**: `<rowId>-<columnId>` — always computed directly, never looked up. **Cell name** (spreadsheet-style, needed for the MCP tool in Step 7b): read, never computed from `columnIndex`/`actorRowIndex`. Call `mcp__eventmodelers__get_board_outline { "boardId": "<BOARD_ID>", "chapterId": "<CHAPTER_ID>" }` once and take the target column's `letter` plus the actor lane's 1-based position in its `lanes` list (lanes are listed top to bottom) — e.g. `letter: "D"`, actor lane first in `lanes` → `D1`.
|
|
264
264
|
|
|
265
265
|
---
|
|
266
266
|
|
|
@@ -272,7 +272,7 @@ For each screen:
|
|
|
272
272
|
|
|
273
273
|
### 7a — Acquire a column slot
|
|
274
274
|
|
|
275
|
-
**If the empty-column queue is non-empty** — pop the first entry → `columnId` and its remembered `columnIndex`. Compute `CELL_ID = actorRowId + "-" + columnId` and `CELL_NAME`
|
|
275
|
+
**If the empty-column queue is non-empty** — pop the first entry → `columnId` and its remembered `columnIndex`. Compute `CELL_ID = actorRowId + "-" + columnId` and take `CELL_NAME` from the outline as described above.
|
|
276
276
|
|
|
277
277
|
**If the empty-column queue is empty** — create a new column.
|
|
278
278
|
|
|
@@ -280,11 +280,11 @@ For each screen:
|
|
|
280
280
|
```
|
|
281
281
|
mcp__eventmodelers__add_column { "boardId": "<BOARD_ID>", "timelineId": "<CHAPTER_ID>" }
|
|
282
282
|
```
|
|
283
|
-
|
|
283
|
+
Re-read `get_board_outline` after adding the column and take the new (last) column's `letter` for `CELL_NAME` — don't convert the result's `index` into a letter yourself.
|
|
284
284
|
|
|
285
285
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 7a — Create column".
|
|
286
286
|
|
|
287
|
-
Extract `columnId`. Compute `CELL_ID = actorRowId + "-" + columnId` and `CELL_NAME`
|
|
287
|
+
Extract `columnId`. Compute `CELL_ID = actorRowId + "-" + columnId` and take `CELL_NAME` from the outline as described above.
|
|
288
288
|
|
|
289
289
|
### 7b — Create the SCREEN node, atomically
|
|
290
290
|
|
|
@@ -307,7 +307,7 @@ mcp__eventmodelers__create_screen {
|
|
|
307
307
|
"contentType": "html",
|
|
308
308
|
"nodeId": "<SCREEN_NODE_ID>",
|
|
309
309
|
"chapterId": "<CHAPTER_ID>",
|
|
310
|
-
"
|
|
310
|
+
"cellName": "<CELL_NAME>",
|
|
311
311
|
"title": "<screen.title>",
|
|
312
312
|
"pages": ["<reconstructed HTML fragment for this screen>"],
|
|
313
313
|
"description": "<screen.description — 'Shows X. Arrived via: Y. Actions: user can do A, user can do B.'>"
|
package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md
CHANGED
|
@@ -32,9 +32,11 @@ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
|
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
curl -s -H "x-token: $TOKEN" \
|
|
35
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
35
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
Returns `{rows, columns, cells}` directly (not wrapped in `meta.timelineData`).
|
|
39
|
+
|
|
38
40
|
## Step 7a — Create column
|
|
39
41
|
|
|
40
42
|
```bash
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md
CHANGED
|
@@ -47,20 +47,20 @@ Follow **`eventmodeling-interview-protocol`** to run this interview and record i
|
|
|
47
47
|
|
|
48
48
|
## Board Context
|
|
49
49
|
|
|
50
|
-
Before brainstorming, check for EVENT nodes already on the board to avoid duplicating events from a previous session:
|
|
50
|
+
Before brainstorming, check for EVENT nodes already on the board to avoid duplicating events from a previous session — titles (plus field names) are all a dedupe needs, so use `projection: "line"`:
|
|
51
51
|
|
|
52
52
|
**Prefer MCP:**
|
|
53
53
|
```
|
|
54
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "EVENT" }
|
|
54
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "EVENT", "projection": "line" }
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Context — Check existing EVENT nodes".
|
|
58
58
|
|
|
59
|
-
If events already exist, treat them as the starting list and focus on discovering what might be missing. Also check for existing chapters (timelines) so you can reuse them:
|
|
59
|
+
If events already exist, treat them as the starting list and focus on discovering what might be missing. Also check for existing chapters (timelines) so you can reuse them — ids and titles only, so `projection: "line"` (without it every chapter's full grid comes back):
|
|
60
60
|
|
|
61
61
|
**Prefer MCP:**
|
|
62
62
|
```
|
|
63
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
63
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Context — Check existing CHAPTER nodes".
|
|
@@ -6,14 +6,14 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
curl -s -H "x-token: $TOKEN" \
|
|
9
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
|
|
9
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT&projection=line"
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
## Board Context — Check existing CHAPTER nodes
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
curl -s -H "x-token: $TOKEN" \
|
|
16
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER"
|
|
16
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line"
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Timeline Discovery — Step 2: Create one chapter per group — create the chapter
|
|
@@ -63,8 +63,8 @@ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$CHAPTER_I
|
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
65
|
curl -s -H "x-token: $TOKEN" \
|
|
66
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
67
|
-
# →
|
|
66
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
67
|
+
# → rows — find the row where type === "swimlane" ({rows, columns, cells} directly, not under meta.timelineData)
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
## Mode A — Step D: Create the event with cellId
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md
CHANGED
|
@@ -18,18 +18,17 @@ Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect`
|
|
|
18
18
|
|
|
19
19
|
Before starting, read the current board state to drive the analysis from what is actually on the board rather than relying solely on conversation context:
|
|
20
20
|
|
|
21
|
-
**Prefer MCP
|
|
21
|
+
**Prefer MCP** — the field origin/destination checks need every element's fields plus its dependencies, which the `fields` projection of the slice data returns per slice in one call (no specs, comments or prompts):
|
|
22
22
|
```
|
|
23
|
-
|
|
24
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "COMMAND" }
|
|
25
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "READMODEL" }
|
|
23
|
+
mcp__eventmodelers__get_slice_data { "boardId": "<BOARD_ID>", "contextName": "<CONTEXT_NAME>", "projection": "fields", "format": "toon" }
|
|
26
24
|
```
|
|
25
|
+
Drive it by timeline, not by MODEL_CONTEXT — a timeline with no assigned or connected context is its own context, so listing only the MODEL_CONTEXT nodes misses it. List the timelines (ids/titles only): `mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }`, then pass each timeline's title as `contextName`. The response covers that timeline's whole effective context — every timeline in the same context — and each slice carries its `chapter`, so skip any timeline already named in a response you have (call the rest in parallel).Drop `projection` only for a node whose field types/examples you actually need to inspect — and then scope it with `nodeIds`.
|
|
27
26
|
|
|
28
27
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Context".
|
|
29
28
|
|
|
30
29
|
Use these results as the source of truth for the completeness check.
|
|
31
30
|
|
|
32
|
-
**Before treating any nodes as duplicates**: check each node's `data.linkedTo` field (see `learn-eventmodelers-api`). A node with `linkedTo` set is an intentional **linked copy** of another node — placed elsewhere on the timeline for readability, not a modeling defect. When two or more nodes share a title/type:
|
|
31
|
+
**Before treating any nodes as duplicates**: check each node's `data.linkedTo` field (see `learn-eventmodelers-api`). Neither projection above carries it — fetch the full records of just the same-titled candidates with `mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "nodeIds": [<their ids>] }`. A node with `linkedTo` set is an intentional **linked copy** of another node — placed elsewhere on the timeline for readability, not a modeling defect. When two or more nodes share a title/type:
|
|
33
32
|
- If any of them carries `linkedTo`, do not report a duplicate. This is expected, not a gap.
|
|
34
33
|
- Never propose deleting, suppressing, or "cleaning up" either node in a linked pair. Specifically, never target the node that has *no* `linkedTo` (the original) for removal — copies reference it via `moveToWidget=<originNodeId>`, so deleting it breaks every copy.
|
|
35
34
|
- Only flag same-titled nodes as an actual duplicate gap when **none** of them has `linkedTo` — i.e., they are genuinely two independent, unlinked nodes describing the same concept.
|
|
@@ -53,18 +52,15 @@ Verify every field a read model displays is sourced from a connected event. A fu
|
|
|
53
52
|
|
|
54
53
|
Every column that holds a COMMAND or READMODEL node must have a slice defined (a SLICE_BORDER node on that column) — otherwise it can never be built as a feature. Skip columns whose COMMAND/READMODEL node has `data.linkedTo` set: it's a linked copy (see Board Context above), and only the original's column needs a slice.
|
|
55
54
|
|
|
56
|
-
**Prefer MCP** — `
|
|
55
|
+
**Prefer MCP** — `get_board_outline` answers this directly per chapter: every column with its node list (id, type, title) and a `sliceStatus` when a slice spans it — no need to pull full SLICE_BORDER records and join on `columnId`:
|
|
57
56
|
```
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
Or, to get the full SLICE_BORDER nodes (with `columnId`) the same way as the curl fallback:
|
|
61
|
-
```
|
|
62
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "SLICE_BORDER" }
|
|
57
|
+
mcp__eventmodelers__get_board_outline { "boardId": "<BOARD_ID>", "chapterId": "<CHAPTER_ID>" }
|
|
63
58
|
```
|
|
59
|
+
Get the chapter ids from `mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }` (ids/titles only). For just the slice list with statuses, `mcp__eventmodelers__list_slices { "boardId": "<BOARD_ID>" }` is the lightest read.
|
|
64
60
|
|
|
65
61
|
**Fallback (no MCP):** see `references/api-fallback.md` — "4. Check Slice Coverage".
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
Flag any column that holds a COMMAND or READMODEL node but carries no `sliceStatus` as a gap (skipping linked copies, which are exempt — confirm `linkedTo` on the flagged nodes with a `nodeIds`-scoped full `get_nodes`). A full worked example is in `references/examples.md`.
|
|
68
64
|
|
|
69
65
|
### 5. Check Event Stream Completeness
|
|
70
66
|
Verify no "missing" events:
|
|
@@ -5,18 +5,28 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Board Context
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
#
|
|
8
|
+
# Timelines (ids/titles only) — drive the slice data by timeline: a timeline without a context is its own context
|
|
9
9
|
curl -s -H "x-token: $TOKEN" \
|
|
10
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line"
|
|
11
|
+
|
|
12
|
+
# Elements with fields + dependencies for a timeline's effective context (no specs/comments).
|
|
13
|
+
# Skip timelines already named in a previous response's slice `chapter` values.
|
|
14
|
+
curl -s -G -H "x-token: $TOKEN" \
|
|
15
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata" \
|
|
16
|
+
--data-urlencode "contextName=<TIMELINE_TITLE>" -d projection=fields -d format=toon
|
|
17
|
+
|
|
18
|
+
# Full records (incl. data.linkedTo) for just the duplicate candidates
|
|
19
|
+
curl -s -X POST -H "x-token: $TOKEN" -H "Content-Type: application/json" \
|
|
20
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/query" -d '{"ids": ["<id1>", "<id2>"]}'
|
|
15
21
|
```
|
|
16
22
|
|
|
17
23
|
## 4. Check Slice Coverage
|
|
18
24
|
|
|
25
|
+
There is no REST equivalent of `get_board_outline`, so this fallback keeps the full SLICE_BORDER rows — the cross-reference needs their `columnId`, which `projection=line` does not carry:
|
|
26
|
+
|
|
19
27
|
```bash
|
|
20
28
|
curl -s -H "x-token: $TOKEN" \
|
|
21
29
|
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=SLICE_BORDER"
|
|
22
30
|
```
|
|
31
|
+
|
|
32
|
+
Cross-reference each COMMAND/READMODEL node's column against the `columnId` of the SLICE_BORDER nodes; a column with no matching slice is a gap (linked copies exempt).
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md
CHANGED
|
@@ -327,18 +327,18 @@ After designing all scenarios, post them to the board using the timeline/column
|
|
|
327
327
|
|
|
328
328
|
### Step 1 — Identify the target timeline and column
|
|
329
329
|
|
|
330
|
-
Fetch all CHAPTER nodes to find the timeline.
|
|
330
|
+
Fetch all CHAPTER nodes to find the timeline — ids and titles only, so `projection: "line"` (without it every chapter's full grid comes back).
|
|
331
331
|
|
|
332
332
|
**Prefer MCP:**
|
|
333
333
|
```
|
|
334
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
334
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }
|
|
335
335
|
```
|
|
336
336
|
|
|
337
337
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Post Scenarios to Board — Step 1: Identify the Target Timeline and Column".
|
|
338
338
|
|
|
339
339
|
If there is more than one chapter, ask the user which timeline to target.
|
|
340
340
|
|
|
341
|
-
For each command or view being specified, find its column:
|
|
341
|
+
For each command or view being specified, find its column: `mcp__eventmodelers__get_board_outline { "boardId": "<BOARD_ID>", "chapterId": "<TL>" }` lists every column (`columnId`, index) with the nodes in it — take the column whose node list holds that COMMAND or READMODEL. If the user named the slice, find the SLICE_BORDER node with that title (`get_nodes { "type": "SLICE_BORDER", "name": "<slice title>", "projection": "line" }` gives its id) or its column in the outline to get the `colId`. (REST fallback: `GET .../nodes/$TL?projection=cells` → `{rows, columns, cells}`; find the cell whose `nodeId` is the COMMAND/READMODEL and take its `colId`.)
|
|
342
342
|
|
|
343
343
|
### Step 2 — Load valid step elements
|
|
344
344
|
|
|
@@ -5,7 +5,7 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Post Scenarios to Board — Step 1: Identify the Target Timeline and Column
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER" \
|
|
8
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line" \
|
|
9
9
|
-H "x-token: $TOKEN"
|
|
10
10
|
```
|
|
11
11
|
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md
CHANGED
|
@@ -197,7 +197,7 @@ After `place-element` returns the COMMAND node ID, create the arrows that comple
|
|
|
197
197
|
|
|
198
198
|
1. **SCREEN → COMMAND** — find the SCREEN node in the actor row of the same column.
|
|
199
199
|
|
|
200
|
-
**Prefer MCP** —
|
|
200
|
+
**Prefer MCP** — `get_nodes` has no `cellId` filter (see note below), so read the chapter's cell map instead — `projection: "cells"` returns just `{rows, columns, cells}`, not the whole chapter node:
|
|
201
201
|
```
|
|
202
202
|
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>", "projection": "cells" }
|
|
203
203
|
```
|
|
@@ -224,7 +224,7 @@ After `place-element` returns the COMMAND node ID, create the arrows that comple
|
|
|
224
224
|
|
|
225
225
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Wire connections — Step 2: COMMAND → EVENT".
|
|
226
226
|
|
|
227
|
-
*Note:* `get_nodes` has no `cellId` filter
|
|
227
|
+
*Note:* `get_nodes` filters by `type`, `name` (partial title), `chapterId` and `nodeIds`, but has no `cellId` filter — the `get_node`-on-CHAPTER (`projection: "cells"`) + `cells` lookup above is the only way to check single-cell occupancy via MCP. (The REST `GET .../nodes` does take `cellId`/`colId`, each requiring `timelineId` — see the fallback file.)
|
|
228
228
|
|
|
229
229
|
Skip a connection silently if the target cell is empty (the element may be placed in a later step). Log each created arrow: `→ connected SCREEN→COMMAND "PlaceOrder"` or `→ connected COMMAND→EVENT "PlaceOrder"→"OrderPlaced"`.
|
|
230
230
|
|
|
@@ -14,8 +14,8 @@ curl -s -H "x-token: $TOKEN" \
|
|
|
14
14
|
**Step B — Fetch the chapter to find the interaction row ID:**
|
|
15
15
|
```bash
|
|
16
16
|
curl -s -H "x-token: $TOKEN" \
|
|
17
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
18
|
-
# →
|
|
17
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
18
|
+
# → rows — find the row where type === "interaction" ({rows, columns, cells} directly, not under meta.timelineData)
|
|
19
19
|
```
|
|
20
20
|
Save `interactionRow.id`.
|
|
21
21
|
|
|
@@ -51,7 +51,7 @@ curl -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
|
|
|
51
51
|
## Wire connections — Step 1: SCREEN → COMMAND
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<actorRowId>-<columnId
|
|
54
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<actorRowId>-<columnId>&timelineId=$CHAPTER_ID" \
|
|
55
55
|
-H "x-token: $TOKEN" -H "x-user-id: eventmodeling-identifying-inputs"
|
|
56
56
|
```
|
|
57
57
|
If a SCREEN node exists, connect it:
|
|
@@ -65,7 +65,7 @@ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/connections" \
|
|
|
65
65
|
## Wire connections — Step 2: COMMAND → EVENT
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<swimlaneRowId>-<columnId
|
|
68
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<swimlaneRowId>-<columnId>&timelineId=$CHAPTER_ID" \
|
|
69
69
|
-H "x-token: $TOKEN" -H "x-user-id: eventmodeling-identifying-inputs"
|
|
70
70
|
```
|
|
71
71
|
Connect command to its resulting event:
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md
CHANGED
|
@@ -195,7 +195,7 @@ Before finalizing any read model, ask: "does this screen contain more than one c
|
|
|
195
195
|
|
|
196
196
|
**Do not re-derive read model needs from a screen's title or description alone, and do not rely on the orchestrator's phase-summary handoff for this** — if you arrived here via `eventmodeling-orchestrating-event-modeling`, the handoff after Step 3 is a short hand-written prose summary (`.eventmodelers/interviews/.../EVENTMODELING.md`), not the actual field data. It will not reliably carry the per-field mappings forward. Go back to the board itself:
|
|
197
197
|
|
|
198
|
-
For every SCREEN node, fetch it directly (`get_node`/`get_nodes`, never from memory) and read its `meta.fields
|
|
198
|
+
For every SCREEN node, fetch it directly (`get_node`/`get_nodes`, never from memory) and read its `meta.fields` — a full read on purpose (`projection: "line"` carries field names only, not the `mapping` this step groups by); scope it to the chapter, e.g. `get_nodes { "boardId": "<BOARD_ID>", "type": "SCREEN", "chapterId": "<CHAPTER_ID>" }`, rather than board-wide. Step 3 already required every field to carry a `mapping`, and for view fields that mapping is already in the exact form `"<ReadModelTitle>.<fieldName>"` — recorded specifically so this step doesn't have to re-guess it.
|
|
199
199
|
|
|
200
200
|
- **Group the screen's fields by the `<ReadModelTitle>` already named in their `mapping`.** That grouping — not a fresh read of the screen's visuals — is the read model's title and field list. Build the READMODEL node from it directly.
|
|
201
201
|
- If a field's `mapping` names a read model that isn't `"<CommandTitle>.<fieldName>"` or `"session:..."` or `"derived:..."`, it is a read-model reference — treat it as a requirement, not a suggestion.
|
|
@@ -273,7 +273,7 @@ mcp__eventmodelers__submit_node_events {
|
|
|
273
273
|
}]
|
|
274
274
|
}
|
|
275
275
|
```
|
|
276
|
-
To determine the consumer's column index beforehand, or to check whether a specific interaction cell is already occupied (
|
|
276
|
+
To determine the consumer's column index beforehand, or to check whether a specific interaction cell is already occupied (`get_nodes` filters by `type`, `name`, `chapterId` and `nodeIds`, but has no `cellId` filter — see the note under "Wire connections" below), fetch the chapter and read its cell map — `projection: "cells"` returns just `{rows, columns, cells}`, not the whole chapter node:
|
|
277
277
|
```
|
|
278
278
|
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>", "projection": "cells" }
|
|
279
279
|
# → rows (find "interaction"/"actor" rows) and cells (sparse; absent id = empty)
|
|
@@ -323,7 +323,7 @@ After `place-element` returns the READMODEL node ID, create the arrows that comp
|
|
|
323
323
|
|
|
324
324
|
1. **EVENT → READMODEL** — find the primary source EVENT node in the swimlane row of the same column.
|
|
325
325
|
|
|
326
|
-
**Prefer MCP** — `get_nodes` has no `cellId` filter (
|
|
326
|
+
**Prefer MCP** — `get_nodes` has no `cellId` filter (it filters by `type`, `name`, `chapterId`, `nodeIds` only); look up occupancy via the chapter's cell map instead (`projection: "cells"`), then connect with the type-checked edge tool (auto-corrects direction, skips duplicates):
|
|
327
327
|
```
|
|
328
328
|
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>", "projection": "cells" }
|
|
329
329
|
# → read cells["<swimlaneRowId>-<columnId>"] for the occupying node id
|
|
@@ -369,7 +369,7 @@ If this step is processing more than one read model in the same pass, collect ev
|
|
|
369
369
|
|
|
370
370
|
### Step 5i — Mandatory per-node verification (run before declaring this step done)
|
|
371
371
|
|
|
372
|
-
Do not declare Step 5 complete on the strength of the read models you happened to design. Instead, **re-fetch every SCREEN and AUTOMATION node on the board** (
|
|
372
|
+
Do not declare Step 5 complete on the strength of the read models you happened to design. Instead, **re-fetch every SCREEN and AUTOMATION node on the board** (don't rely on the list built earlier in this step, the board may have moved on) and check each one individually. Questions 1–3 need only node types and edges: `get_board_outline { "boardId": "<BOARD_ID>", "chapterId": "<CHAPTER_ID>" }` per chapter returns every node per column plus the flat edge list — the full node records from `get_nodes` carry no edges. Read a node in full (`get_node`) only when you fix a gap and need its `meta.fields`/`mapping`:
|
|
373
373
|
|
|
374
374
|
1. Does it now have an incoming `READMODEL → SCREEN` or `READMODEL → AUTOMATION` connection?
|
|
375
375
|
2. If it's a SCREEN and not connected — is it a provably blank creation form with no prior state? State the reason in one line (e.g. `"Register Account" screen: blank form, no prior state — exempt`). This exemption applies to screens only.
|
|
@@ -7,12 +7,12 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
7
7
|
1. Find the column where the consumer SCREEN or AUTOMATION lives. For an AUTOMATION, the read model's target column is always the one immediately **before** it (skip straight to inserting that column — its interaction row is guaranteed occupied by the automation's own COMMAND). For a SCREEN, target the screen's own column. Fetch the timeline to get the interaction row ID:
|
|
8
8
|
```bash
|
|
9
9
|
curl -s -H "x-token: $TOKEN" \
|
|
10
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
11
|
-
# →
|
|
10
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
11
|
+
# → rows — find the row where type === "interaction" ({rows, columns, cells} directly, not under meta.timelineData)
|
|
12
12
|
```
|
|
13
13
|
2. Check if the target interaction cell is already occupied (existing COMMAND):
|
|
14
14
|
```bash
|
|
15
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<interactionRowId>-<columnId
|
|
15
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<interactionRowId>-<columnId>&timelineId=$CHAPTER_ID" \
|
|
16
16
|
-H "x-token: $TOKEN"
|
|
17
17
|
```
|
|
18
18
|
If a COMMAND occupies that cell, insert a new column immediately **before** it (`{"index": currentIndex}` — this shifts the consumer's column, and everything after it, one to the right) and use that new column's ID instead. The read model must end up upstream of (to the left of) its consumer, never downstream of it.
|
|
@@ -35,7 +35,7 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
35
35
|
## Step 5h.1 — Wire EVENT → READMODEL
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<swimlaneRowId>-<columnId
|
|
38
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<swimlaneRowId>-<columnId>&timelineId=$CHAPTER_ID" \
|
|
39
39
|
-H "x-token: $TOKEN" -H "x-user-id: eventmodeling-identifying-outputs"
|
|
40
40
|
```
|
|
41
41
|
Connect it:
|