@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
|
@@ -91,11 +91,11 @@ For each timeline in scope, check all node types that should be in cells.
|
|
|
91
91
|
```
|
|
92
92
|
mcp__eventmodelers__validate_model { "boardId": "$BOARD_ID", "chapterId": "$TIMELINE_ID" }
|
|
93
93
|
```
|
|
94
|
-
Only fall back to per-type `get_nodes` (`chapterId`-scoped, once each for `EVENT`, `COMMAND`, `READMODEL`, `SCREEN`, `AUTOMATION`) when you also need the node bodies for another reason in the same pass.
|
|
94
|
+
Only fall back to per-type `get_nodes` (`chapterId`-scoped, once each for `EVENT`, `COMMAND`, `READMODEL`, `SCREEN`, `AUTOMATION`) when you also need the node bodies for another reason in the same pass — otherwise add `"projection": "line"` (it carries no `cellName`): a returned id that `get_board_outline` for that chapter doesn't list is the unplaced one.
|
|
95
95
|
|
|
96
96
|
**Fallback (no MCP):** see `references/api-fallback.md` — "No unplaced elements (0,0 nodes) — Scan for unplaced nodes".
|
|
97
97
|
|
|
98
|
-
For each returned node, check whether it has a valid cell assignment. A node without a `cellId` (or with `chapterId` missing) is unplaced
|
|
98
|
+
For each returned node, check whether it has a valid cell assignment. A node without a `cellId` (or with `chapterId` missing) is unplaced; a `line`-projected result has no cell info, so check it against `get_board_outline`, which lists every placed node with its `cellName`.
|
|
99
99
|
|
|
100
100
|
**For each unplaced node:**
|
|
101
101
|
- **If it belongs in the current model** → compute the correct `cellId` and place it.
|
|
@@ -152,7 +152,7 @@ Each write tool takes its items as an array, so handling several in one pass is
|
|
|
152
152
|
- `add_column`'s `count` param (not `add_column` repeated) — appending or inserting several columns at once; `beforeNodeId`/`afterNodeId` resolve the insertion point from an already-placed node instead of a computed index
|
|
153
153
|
- `create_chapter`'s `columns` param — when the chapter's initial column count is already known, instead of creating the default 3 and appending more after
|
|
154
154
|
|
|
155
|
-
Also prefer `get_nodes`' `chapterId` param over an unscoped board-wide fetch whenever the step is working within one timeline (the common case), and `get_node`'s `projection: "cells"` over a full chapter fetch whenever only `{rows, columns, cells}` is needed (most cell/column bookkeeping lookups).
|
|
155
|
+
Also prefer `get_nodes`' `chapterId` param over an unscoped board-wide fetch whenever the step is working within one timeline (the common case), and `get_node`'s `projection: "cells"` over a full chapter fetch whenever only `{rows, columns, cells}` is needed (most cell/column bookkeeping lookups). When only ids/titles/types/attribute names matter (existence check, dedupe by title, picking a chapter or context, finding a node by name), pass `projection: "line"` to `get_nodes` — `{id, type, title, fields}` per node; cell addresses come only from `get_board_outline`.
|
|
156
156
|
|
|
157
157
|
For a "what is on the board and how is it wired right now" check between steps — the common orientation read, and the input to choosing an insertion anchor — use `get_board_outline` (`{boardId, chapterId}`). It returns one compact object: per-column node lists (`{id, type, title, lane}`) plus a flat edge list, with no rendered screen HTML or field bodies. Reserve full `get_nodes` (no projection) for when you actually need a node's `meta.fields` or page content.
|
|
158
158
|
|
|
@@ -7,10 +7,12 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
7
7
|
```bash
|
|
8
8
|
for TYPE in EVENT COMMAND READMODEL SCREEN AUTOMATION; do
|
|
9
9
|
curl -s -H "x-token: $TOKEN" \
|
|
10
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=$TYPE"
|
|
10
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=$TYPE&projection=line"
|
|
11
11
|
done
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
`projection=line` returns `{id, type, title, fields?}` per node — no cell info. To find the unplaced ones, read each chapter's grid (`GET .../nodes/$CHAPTER_ID?projection=cells`) and collect every `cells[].nodeId`; a node id that appears in no chapter's `cells` is unplaced.
|
|
15
|
+
|
|
14
16
|
## No unplaced elements (0,0 nodes) — Place a found unplaced node
|
|
15
17
|
|
|
16
18
|
```bash
|
|
@@ -107,9 +107,9 @@ Timelines (chapters) are **created and assigned during Step 1 (Brainstorming)**.
|
|
|
107
107
|
|
|
108
108
|
Before placing events, resolve the target timeline:
|
|
109
109
|
|
|
110
|
-
Prefer MCP:
|
|
110
|
+
Prefer MCP — ids and titles are enough to pick one, so `projection: "line"` (without it every chapter's full grid comes back):
|
|
111
111
|
```
|
|
112
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "CHAPTER" }
|
|
112
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "CHAPTER", "projection": "line" }
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Chapters and Timelines — Resolve the Target Timeline".
|
|
@@ -6,5 +6,5 @@ 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=CHAPTER"
|
|
9
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line"
|
|
10
10
|
```
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md
CHANGED
|
@@ -47,9 +47,9 @@ Walk the timeline column by column:
|
|
|
47
47
|
|
|
48
48
|
`$TL` (the timeline/chapter UUID) is required for every call below. If it wasn't given up front, resolve it before doing anything else:
|
|
49
49
|
|
|
50
|
-
Prefer MCP:
|
|
50
|
+
Prefer MCP — only ids and titles are needed to pick a chapter, so use the `line` projection (without it every chapter's full grid comes back):
|
|
51
51
|
```
|
|
52
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
52
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 1: Resolve the Timeline".
|
|
@@ -69,7 +69,7 @@ mcp__eventmodelers__get_spec_info { "boardId": "<BOARD_ID>", "timelineId": "<TL>
|
|
|
69
69
|
|
|
70
70
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 2: Enumerate Commands, Read Models, and Automations — spec-info".
|
|
71
71
|
|
|
72
|
-
Separately, enumerate AUTOMATION nodes via `get_nodes { "boardId": "<BOARD_ID>", "type": "AUTOMATION", "chapterId": "<TL>" }` — `spec-info` cannot return them.
|
|
72
|
+
Separately, enumerate AUTOMATION nodes via `get_nodes { "boardId": "<BOARD_ID>", "type": "AUTOMATION", "chapterId": "<TL>", "projection": "line" }` — `spec-info` cannot return them; `line` is enough since only id and title are needed (the column comes from the chapter's `cells` below).
|
|
73
73
|
|
|
74
74
|
`spec-info` doesn't include the column each element sits in, so fetch the chapter node to resolve it — `projection: "cells"` returns just `{rows, columns, cells}`, not the whole chapter node:
|
|
75
75
|
|
|
@@ -5,7 +5,8 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Step 1: Resolve the Timeline
|
|
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" -H "x-token: $TOKEN"
|
|
9
|
+
# → [{ id, type, title }] — enough to pick a chapter
|
|
9
10
|
```
|
|
10
11
|
|
|
11
12
|
## Step 2: Enumerate Commands, Read Models, and Automations — spec-info
|
|
@@ -15,12 +16,19 @@ curl "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/timelines/$TL/spec-info" -H "x-
|
|
|
15
16
|
# → { timelineId, elements: [{ id, title, type }] } — filter client-side to type in COMMAND, READMODEL (no elementTypes param over REST)
|
|
16
17
|
```
|
|
17
18
|
|
|
19
|
+
AUTOMATION nodes in the timeline:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=AUTOMATION&chapterId=$TL&projection=line" -H "x-token: $TOKEN"
|
|
23
|
+
# → [{ id, type, title, fields? }]
|
|
24
|
+
```
|
|
25
|
+
|
|
18
26
|
## Step 2: Enumerate Commands, Read Models, and Automations — Chapter Node
|
|
19
27
|
|
|
20
28
|
```bash
|
|
21
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TL" -H "x-token: $TOKEN"
|
|
22
|
-
# →
|
|
23
|
-
# →
|
|
29
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TL?projection=cells" -H "x-token: $TOKEN"
|
|
30
|
+
# → columns: [{ id, index }]
|
|
31
|
+
# → cells: [{ id: "<rowId>-<columnId>", nodeId }]
|
|
24
32
|
```
|
|
25
33
|
|
|
26
34
|
## Step 2: Enumerate Commands, Read Models, and Automations — Check Existing Slices
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md
CHANGED
|
@@ -90,12 +90,12 @@ Solution: Every event includes timestamp
|
|
|
90
90
|
|
|
91
91
|
## Board Integration
|
|
92
92
|
|
|
93
|
-
Before starting the analysis, read existing screen nodes from the board to avoid designing screens that already exist. Screens created by this skill default to HTML_SCREEN, but older boards may still have plain SCREEN (sketch) nodes — check both types:
|
|
93
|
+
Before starting the analysis, read existing screen nodes from the board to avoid designing screens that already exist. Screens created by this skill default to HTML_SCREEN, but older boards may still have plain SCREEN (sketch) nodes — check both types. Titles and field names are all this dedupe needs, so use `projection: "line"` (no HTML bodies); read a screen in full with `get_node` only if you must inspect its design:
|
|
94
94
|
|
|
95
95
|
**Prefer MCP:**
|
|
96
96
|
```
|
|
97
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "HTML_SCREEN" }
|
|
98
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "SCREEN" }
|
|
97
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "HTML_SCREEN", "projection": "line" }
|
|
98
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "SCREEN", "projection": "line" }
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Integration — Check existing screen nodes".
|
|
@@ -219,8 +219,9 @@ Every screen node requires rendered content. **HTML_SCREEN (via the `html-screen
|
|
|
219
219
|
1. Determine the target column (same column as the event/command for a command/input screen, OR the same column as the read model for a view/output screen — one column to the right only if that shared column isn't available).
|
|
220
220
|
2. `actorRowId = roleLaneMap[<this screen's role>]` — the map was already resolved once for the whole chapter; do not re-fetch the chapter per screen. If this screen's role is genuinely new (wasn't in the original Role Catalog), resolve/create its lane now the same way (see above) and add it to the map before continuing.
|
|
221
221
|
3. `cellId = actorRowId + "-" + columnId`
|
|
222
|
+
4. For MCP `create_screen`, which takes only `cellName`: never derive it from array positions — read it from `get_board_outline` for that chapter (call it once if you don't already hold it): the column letter is the target column's `letter` there, the row number is the target lane's 1-based position in its `lanes` list (lanes are listed top to bottom, so the first lane is row 1) (e.g. column `letter: "E"`, this role's actor lane second in `lanes` → `E2`).
|
|
222
223
|
|
|
223
|
-
**Step B (default) — Create the HTML_SCREEN node and render it in one atomic call.** Use `create_screen` with `contentType: "html"` — this creates the node, places it in
|
|
224
|
+
**Step B (default) — Create the HTML_SCREEN node and render it in one atomic call.** Use `create_screen` with `contentType: "html"` — this creates the node, places it in that cell, and renders its pages together, so there is no window where the node exists without content:
|
|
224
225
|
|
|
225
226
|
**Prefer MCP:**
|
|
226
227
|
```
|
|
@@ -230,7 +231,7 @@ mcp__eventmodelers__create_screen {
|
|
|
230
231
|
"contentType": "html",
|
|
231
232
|
"nodeId": "<node-uuid>",
|
|
232
233
|
"chapterId": "<CHAPTER_ID>",
|
|
233
|
-
"
|
|
234
|
+
"cellName": "<cellName, e.g. E2>",
|
|
234
235
|
"title": "<Screen Title>",
|
|
235
236
|
"pages": ["<div>...</div>"],
|
|
236
237
|
"description": "<concise description of what this screen shows>",
|
|
@@ -394,7 +395,7 @@ Older versions of this skill wrote the storyboard as a markdown document (swimla
|
|
|
394
395
|
- [ ] **Every human role from the Role Catalog has at least one swimlane**
|
|
395
396
|
- [ ] **Every human-role swimlane is labeled with the role name from the catalog**
|
|
396
397
|
- [ ] **Swimlanes organized by actor/system in the narrative report**
|
|
397
|
-
- [ ] **Every human role's swimlane is a real, distinct `actor`-type lane on the board (`
|
|
398
|
+
- [ ] **Every human role's swimlane is a real, distinct `actor`-type lane on the board (the chapter's `rows`), not just a grouping in the markdown report** — no two different human roles share the same `actorRowId`
|
|
398
399
|
- [ ] **No system actor / processor has been given its own labeled actor lane** — every AUTOMATION node sits in the chapter's default actor lane, never a lane fabricated to mimic a human role's
|
|
399
400
|
- [ ] **Human role screens clearly separated from processor screens**
|
|
400
401
|
- [ ] **Processor todo list pattern shown for automated systems**
|
|
@@ -6,16 +6,17 @@ 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=HTML_SCREEN"
|
|
9
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=HTML_SCREEN&projection=line"
|
|
10
10
|
curl -s -H "x-token: $TOKEN" \
|
|
11
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=SCREEN"
|
|
11
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=SCREEN&projection=line"
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Resolve One Actor Lane Per Human Role — Step 1: Fetch the chapter's actor rows
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
curl -s -H "x-token: $TOKEN" \
|
|
18
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
18
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
19
|
+
# → {rows, columns, cells} directly — collect every row where type === "actor"
|
|
19
20
|
```
|
|
20
21
|
|
|
21
22
|
## Resolve One Actor Lane Per Human Role — Step 3: Create a new actor lane
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md
CHANGED
|
@@ -27,13 +27,29 @@ The source can be also determined by looking at the defined Scenarios. Are all S
|
|
|
27
27
|
|
|
28
28
|
Before starting, read the current board state to validate what is actually on the board.
|
|
29
29
|
|
|
30
|
-
Prefer MCP
|
|
30
|
+
Prefer MCP (no header wiring needed, auth resolves from the connected session). Pick the read by what each check needs:
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "
|
|
36
|
-
|
|
32
|
+
- **Naming, past tense, duplicates, attribute names** — only titles, types and field names are needed, so call `get_nodes` once per type with the `line` projection (`{id, type, title, fields}`, `fields` = attribute names):
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "EVENT", "projection": "line" }
|
|
36
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "COMMAND", "projection": "line" }
|
|
37
|
+
mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "READMODEL", "projection": "line" }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **Field-source traceability** (every command field comes from a connected read model or is marked generated) — needs field bodies and dependencies, so use the slice data `fields` projection per context:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>", "projection": "fields", "format": "toon" }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- **Scenario coverage** — use the `specs` projection (given/when/then per slice, no elements):
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT_NAME>", "projection": "specs", "format": "toon" }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`specs` (like `outline`) only encodes as `json`, `yaml` or `toon`. Context names: `get_nodes { "type": "MODEL_CONTEXT", "projection": "line" }`, or a timeline name.
|
|
37
53
|
|
|
38
54
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Context".
|
|
39
55
|
|
|
@@ -5,10 +5,19 @@ 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
|
+
# Naming / duplicates / attribute names — compact per-type lists
|
|
8
9
|
curl -s -H "x-token: $TOKEN" \
|
|
9
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
|
|
10
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT&projection=line"
|
|
10
11
|
curl -s -H "x-token: $TOKEN" \
|
|
11
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
|
|
12
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND&projection=line"
|
|
12
13
|
curl -s -H "x-token: $TOKEN" \
|
|
13
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
|
|
14
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL&projection=line"
|
|
15
|
+
|
|
16
|
+
# Field-source traceability — fields + dependencies per slice
|
|
17
|
+
curl -s -H "x-token: $TOKEN" \
|
|
18
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=fields&format=toon"
|
|
19
|
+
|
|
20
|
+
# Scenario coverage — specifications per slice (format must be json|yaml|toon)
|
|
21
|
+
curl -s -H "x-token: $TOKEN" \
|
|
22
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=specs&format=toon"
|
|
14
23
|
```
|
|
@@ -38,13 +38,25 @@ Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect`
|
|
|
38
38
|
|
|
39
39
|
Read the current board state before running the checklist:
|
|
40
40
|
|
|
41
|
-
**Prefer MCP
|
|
41
|
+
**Prefer MCP** — pick the read by what the phase checks:
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "
|
|
47
|
-
|
|
43
|
+
- **Name/type checks** (Phase 1.1 timeline ownership by name, Phase 2.3 past tense, duplicate names) — `get_nodes` once per type with the `line` projection (`{id, type, title, fields}`, `fields` = attribute names only):
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "EVENT", "projection": "line" }
|
|
47
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "COMMAND", "projection": "line" }
|
|
48
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "READMODEL", "projection": "line" }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- **Field-level checks** (Phase 2.1/2.2 computed or mutable event data, Phase 3 read-model sources, Phase 5 traceability, Phase 7 structural shapes via dependencies) — the slice data `fields` projection per context: elements with fields and dependencies, no specs/comments:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
mcp__eventmodelers__get_slice_data { "boardId": "<BOARD_ID>", "contextName": "<CONTEXT_NAME>", "projection": "fields", "format": "toon" }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- **Business rules / scenarios** (Phase 4) — add `"projection": "specs"` (format `json`, `yaml` or `toon` only).
|
|
58
|
+
|
|
59
|
+
Context names come from `get_nodes { "type": "MODEL_CONTEXT", "projection": "line" }` (or use a timeline name).
|
|
48
60
|
|
|
49
61
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Board Context".
|
|
50
62
|
|
|
@@ -5,10 +5,19 @@ 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
|
+
# Name/type checks — compact per-type lists
|
|
8
9
|
curl -s -H "x-token: $TOKEN" \
|
|
9
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
|
|
10
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT&projection=line"
|
|
10
11
|
curl -s -H "x-token: $TOKEN" \
|
|
11
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
|
|
12
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND&projection=line"
|
|
12
13
|
curl -s -H "x-token: $TOKEN" \
|
|
13
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
|
|
14
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL&projection=line"
|
|
15
|
+
|
|
16
|
+
# Field-level checks — fields + dependencies per slice
|
|
17
|
+
curl -s -H "x-token: $TOKEN" \
|
|
18
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=fields&format=toon"
|
|
19
|
+
|
|
20
|
+
# Business rules / scenarios (format must be json|yaml|toon)
|
|
21
|
+
curl -s -H "x-token: $TOKEN" \
|
|
22
|
+
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/slicedata?contextName=<CONTEXT_NAME>&projection=specs&format=toon"
|
|
14
23
|
```
|
|
@@ -42,8 +42,8 @@ A run that opens each element with its own `get_node` to "read existing examples
|
|
|
42
42
|
Resolve `target` to a node, with whichever identifier it is:
|
|
43
43
|
|
|
44
44
|
- `target` is a UUID → `mcp__eventmodelers__get_node { "boardId": "$BOARD_ID", "nodeId": "<target>" }`
|
|
45
|
-
- `target` is a name → `mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "name": "<target>" }` — a partial, case-insensitive title match; prefer an exact title, and if several elements still match, list them and ask the user to pick one
|
|
46
|
-
- `target` is a cell name (e.g. `B3`) → `
|
|
45
|
+
- `target` is a name → `mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "name": "<target>", "projection": "line" }` — a partial, case-insensitive title match; `line` is enough to pick (ids/titles/types only); prefer an exact title, and if several elements still match, list them and ask the user to pick one. Then read the chosen node in full with `get_node` (Step 4 needs its fields' types and examples).
|
|
46
|
+
- `target` is a cell name (e.g. `B3`) → `mcp__eventmodelers__get_board_outline { "boardId": "$BOARD_ID", "chapterId": "$CHAPTER_ID" }` (the only read that reports `cellName`) and take the node whose `cellName` is `target`, then read it in full by its id with `get_node` (if multiple chapters exist, find the chapter first with `get_nodes { "type": "CHAPTER", "projection": "line" }` and ask the user if ambiguous). If you already did the full chapter read from Step 1b, take the node by that id from there instead of `get_node`. Never construct a cell address yourself — only use one you were given or read back from `get_board_outline`; from then on address the element by its node id.
|
|
47
47
|
|
|
48
48
|
Then continue with Step 3 (linked elements) — `mcp__eventmodelers__get_connected_nodes { "boardId": "$BOARD_ID", "nodeId": "<resolved id>", "includeFields": true }` returns the neighbours and their fields in one call.
|
|
49
49
|
|
|
@@ -15,15 +15,15 @@ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$target" \
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
### 2b — Name search
|
|
18
|
-
If `target` is not a UUID,
|
|
18
|
+
If `target` is not a UUID, search by title with the `name` filter (partial, case-insensitive) — `projection=line` returns just `{id, type, title, fields}` per match, enough to pick one:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes" \
|
|
22
|
-
-
|
|
23
|
-
|
|
21
|
+
curl -s -G "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes" \
|
|
22
|
+
--data-urlencode "name=$target" --data-urlencode "projection=line" \
|
|
23
|
+
-H "x-user-id: examples-skill"
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Pick the best match (exact title match preferred; case-insensitive). If multiple matches exist, list them and ask the user to pick one.
|
|
26
|
+
Pick the best match (exact `title` match preferred; case-insensitive). If multiple matches exist, list them and ask the user to pick one. Then fetch the chosen node in full as in 2a — Step 4 needs its `meta.fields` with types and examples.
|
|
27
27
|
|
|
28
28
|
### 2c — Cell name (spreadsheet-style, e.g. "B3")
|
|
29
29
|
If `target` matches the pattern `[A-Z]+[0-9]+`:
|
|
@@ -31,7 +31,7 @@ If `target` matches the pattern `[A-Z]+[0-9]+`:
|
|
|
31
31
|
1. Fetch all chapters on the board to find the relevant timeline:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER" \
|
|
34
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER&projection=line" \
|
|
35
35
|
-H "x-user-id: examples-skill"
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -40,20 +40,20 @@ If multiple chapters exist, ask the user which one to use.
|
|
|
40
40
|
2. Fetch the chapter **fresh** to decode the grid — never use cached chapter data, as cells are updated frequently:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID" \
|
|
43
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells" \
|
|
44
44
|
-H "x-user-id: examples-skill"
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
From `meta.timelineData
|
|
47
|
+
From the returned `{rows, columns, cells}` (not wrapped in `meta.timelineData`), decode the cell name into a `cellId`:
|
|
48
48
|
- Column letter(s) → 0-based column index (A=0, B=1, … Z=25, AA=26, …)
|
|
49
49
|
- Row digit → 0-based row index (1→0, 2→1, …)
|
|
50
50
|
- Find the matching column in `columns` and the matching row in `rows`.
|
|
51
51
|
- Compute: **`CELL_ID = row.id + "-" + column.id`** (cell IDs are always `<rowId>-<columnId>`).
|
|
52
52
|
|
|
53
|
-
3. **Always fetch the cell live** to get the current node list — do not rely on the `nodeId` in the chapter's cell data, as it may be stale. No MCP equivalent: `get_nodes`
|
|
53
|
+
3. **Always fetch the cell live** to get the current node list — do not rely on the `nodeId` in the chapter's cell data, as it may be stale. No MCP equivalent: `get_nodes` filters by `type`, `name`, `chapterId` and `nodeIds`, but not `cellId`. The REST `cellId` filter requires `timelineId`:
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID" \
|
|
56
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID&timelineId=$CHAPTER_ID" \
|
|
57
57
|
-H "x-user-id: examples-skill"
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -61,21 +61,27 @@ Use the first non-CHAPTER result (filter out CHAPTER type).
|
|
|
61
61
|
|
|
62
62
|
If no element is found after all strategies, stop and tell the user what was tried.
|
|
63
63
|
|
|
64
|
-
Save the resolved node as `TARGET_NODE` (full JSON including `id
|
|
64
|
+
Save the resolved node as `TARGET_NODE` (full JSON including `id` and `meta`).
|
|
65
65
|
|
|
66
66
|
## Step 3 — Load linked elements for context
|
|
67
67
|
|
|
68
68
|
Collect nearby elements to understand the domain context and generate consistent examples. **Never fetch all board nodes.** Only fetch specific nodes you already have IDs for.
|
|
69
69
|
|
|
70
70
|
### 3a — Nodes from edges
|
|
71
|
-
|
|
71
|
+
Read the target's connections only (`projection=edges` → `{nodeId, edges}`, no node content; the full node record carries no edges):
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes
|
|
74
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TARGET_ID?projection=edges" \
|
|
75
75
|
-H "x-user-id: examples-skill"
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
If `edges` is non-empty, fetch every connected node (the `source`/`target` that isn't `TARGET_ID`) in full — Step 3c needs their field examples — in one call:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/query" \
|
|
82
|
+
-H "Content-Type: application/json" -H "x-user-id: examples-skill" \
|
|
83
|
+
-d '{"ids": ["<EDGE_NODE_ID>", "..."]}'
|
|
84
|
+
```
|
|
79
85
|
|
|
80
86
|
### 3b — Nodes from the same column (cell-based resolution only)
|
|
81
87
|
If you resolved `TARGET_NODE` via a cell name (Step 2c), you already have the full chapter `cells` array in memory. Use it — **no extra API call needed**:
|
|
@@ -64,10 +64,10 @@ This tool finds or creates an empty cell in the correct lane and places the node
|
|
|
64
64
|
|
|
65
65
|
## Step 2 — Resolve the timeline
|
|
66
66
|
|
|
67
|
-
**Prefer MCP
|
|
67
|
+
**Prefer MCP** — ids and titles are all this step needs, so use `projection: "line"` (without it every chapter's full grid comes back):
|
|
68
68
|
|
|
69
69
|
```
|
|
70
|
-
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER" }
|
|
70
|
+
mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "CHAPTER", "projection": "line" }
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 2 — Discover chapters". If `timelineId` is not provided, this discovers chapters on the board.
|
|
@@ -90,7 +90,7 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<TIMELINE_ID>
|
|
|
90
90
|
|
|
91
91
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 3 — Fetch the chapter node".
|
|
92
92
|
|
|
93
|
-
Read `columns` (ordered array of column objects with `id` and `index`) and `cells` (from the result
|
|
93
|
+
Read `columns` (ordered array of column objects with `id` and `index`) and `cells` (directly from the result — MCP and the REST fallback's `?projection=cells` return the same `{rows, columns, cells}` shape).
|
|
94
94
|
|
|
95
95
|
Then resolve `position`:
|
|
96
96
|
|
|
@@ -191,7 +191,7 @@ Save `columnId` from the response.
|
|
|
191
191
|
|
|
192
192
|
## Step 6 — Compute the target cell ID and check availability
|
|
193
193
|
|
|
194
|
-
Using the `
|
|
194
|
+
Using the `{rows, columns, cells}` grid already fetched in Step 3 (re-fetch if a column was just created):
|
|
195
195
|
|
|
196
196
|
- Find the row in `rows` whose `type` matches the target lane (`swimlane`, `interaction`, or `actor`).
|
|
197
197
|
- Compute the cell ID directly: **`CELL_ID = targetRow.id + "-" + columnId`**
|
|
@@ -208,7 +208,7 @@ Cell IDs are always `<rowId>-<columnId>` — no cell array search needed.
|
|
|
208
208
|
|
|
209
209
|
**Fastest path**: use the `cells` array you already fetched in Step 3 via `get_node` (`projection: "cells"`) on the chapter/timeline node — `cells` is a sparse array, so a `nodeId` absent from the entry for `CELL_ID` means the cell is empty. Also scan it for the column-wide conflict from the rule above: any entry with `colId === columnId` and `nodeType === elementType` (for COMMAND/READMODEL/SCREEN/AUTOMATION) means the column is occupied even though `CELL_ID` itself is free. This avoids an extra round-trip, but can be stale if a column was just created in Step 5 — re-fetch first in that case.
|
|
210
210
|
|
|
211
|
-
**No direct MCP equivalent** for querying by cell/column: `get_nodes`
|
|
211
|
+
**No direct MCP equivalent** for querying by cell/column: `get_nodes` filters by `type`, `name`, `chapterId` and `nodeIds`, but has no `cellId`/`colId` filter. The REST API does support it directly though — `GET /nodes` takes `cellId` **or** `colId`, both requiring `timelineId`:
|
|
212
212
|
|
|
213
213
|
```bash
|
|
214
214
|
# Same-cell occupancy — returns [] if empty, or [nodeRecord] if occupied
|
|
@@ -218,7 +218,7 @@ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID&timeli
|
|
|
218
218
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?colId=$COLUMN_ID&timelineId=$TIMELINE_ID"
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
Use this REST call (see the curl fallback below) when you don't already have fresh
|
|
221
|
+
Use this REST call (see the curl fallback below) when you don't already have a fresh grid loaded, instead of re-fetching the chapter just to check occupancy.
|
|
222
222
|
|
|
223
223
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 6 — Check cell occupancy".
|
|
224
224
|
|
|
@@ -281,7 +281,7 @@ mcp__eventmodelers__create_screen {
|
|
|
281
281
|
"contentType": "html",
|
|
282
282
|
"nodeId": "<node-uuid>",
|
|
283
283
|
"chapterId": "<TIMELINE_ID>",
|
|
284
|
-
"
|
|
284
|
+
"cellName": "<CELL_NAME>",
|
|
285
285
|
"title": "<title>",
|
|
286
286
|
"pages": ["<div>...</div>"],
|
|
287
287
|
"description": "<title — what this screen shows>"
|
|
@@ -302,7 +302,7 @@ mcp__eventmodelers__create_screen {
|
|
|
302
302
|
"contentType": "sketch",
|
|
303
303
|
"nodeId": "<node-uuid>",
|
|
304
304
|
"chapterId": "<TIMELINE_ID>",
|
|
305
|
-
"
|
|
305
|
+
"cellName": "<CELL_NAME>",
|
|
306
306
|
"title": "<title>",
|
|
307
307
|
"elements": [...],
|
|
308
308
|
"description": "<title — what this screen shows>"
|
|
@@ -312,7 +312,7 @@ mcp__eventmodelers__create_screen {
|
|
|
312
312
|
|
|
313
313
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 7a — SCREEN: create and render (sketch path, explicit request only)".
|
|
314
314
|
|
|
315
|
-
|
|
315
|
+
MCP `create_screen` takes only `cellName`: pass `CELL_NAME` from Step 1's fast path as-is, or, on the normal path, don't convert Step 6's `CELL_ID` from array positions — read it from `get_board_outline` for that chapter (call it once if you don't already hold it): the column letter is the target column's `letter` there, the row number is the target lane's 1-based position in its `lanes` list (lanes are listed top to bottom, so the first lane is row 1) (e.g. column `letter: "C"`, the target lane second in `lanes` → `C2`). The curl fallback still accepts `cellId`. Expect success (MCP: `created: true`; curl: `204`). On failure, read the validation error, fix the payload, and retry once. Then skip the rest of Step 7 and go to Step 8.
|
|
316
316
|
|
|
317
317
|
### Step 7b — All other element types
|
|
318
318
|
|
|
@@ -372,14 +372,14 @@ Response: `{ "hashes": { "<event-id>": "<hash>" } }` — keyed by the `id` you s
|
|
|
372
372
|
|
|
373
373
|
**A command is never issued by more than one thing.** The server's fire-and-forget auto-connect (`learn-eventmodelers-api` §3) already enforces this — it skips wiring the previous column's SCREEN/AUTOMATION into a COMMAND that already has an inbound trigger, whether that trigger came from the COMMAND's own column or from a pre-existing edge in the DB. Still, run this check whenever `elementType` is `SCREEN`, `AUTOMATION`, or `COMMAND` as a sanity check — e.g. a manual `set_connection` call, or an edge created before this guard existed, can still leave a COMMAND with two issuers.
|
|
374
374
|
|
|
375
|
-
After placing, resolve the relevant COMMAND node (the one just placed, or the one in the same/adjacent column as the SCREEN/AUTOMATION just placed) and inspect its edges:
|
|
375
|
+
After placing, resolve the relevant COMMAND node (the one just placed, or the one in the same/adjacent column as the SCREEN/AUTOMATION just placed) and inspect its edges — only its connections are needed, so use `projection: "edges"` (the full node record carries no edges):
|
|
376
376
|
|
|
377
377
|
**Prefer MCP:**
|
|
378
378
|
```
|
|
379
|
-
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<COMMAND_NODE_ID>" }
|
|
379
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<COMMAND_NODE_ID>", "projection": "edges" }
|
|
380
380
|
```
|
|
381
381
|
|
|
382
|
-
|
|
382
|
+
Take the inbound edges (`target === COMMAND_NODE_ID`) and resolve their source types in one call — only `type` is needed, so `get_nodes { "boardId": "<BOARD_ID>", "nodeIds": [<every inbound source id>], "projection": "line" }`. Count the inbound edges whose source node is type `SCREEN`, `HTML_SCREEN` or `AUTOMATION`.
|
|
383
383
|
|
|
384
384
|
- **0 or 1 such edge** → fine, nothing to do.
|
|
385
385
|
- **2 or more** → keep the edge whose source sits in the COMMAND's own column (the deliberate, same-slice issuer) and remove every other one:
|
package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md
CHANGED
|
@@ -5,15 +5,17 @@ Only needed when MCP is not connected. Every call below has an MCP equivalent in
|
|
|
5
5
|
## Step 2 — Discover chapters
|
|
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
|
```
|
|
10
10
|
|
|
11
11
|
## Step 3 — Fetch the chapter node (columns + cells)
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TIMELINE_ID"
|
|
14
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$TIMELINE_ID?projection=cells"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
Returns `{rows, columns, cells}` directly (not wrapped in `meta.timelineData`).
|
|
18
|
+
|
|
17
19
|
## Step 4a — SCENARIO: append scenarios via the spec endpoint
|
|
18
20
|
|
|
19
21
|
```bash
|
|
@@ -66,7 +68,7 @@ curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID&timeli
|
|
|
66
68
|
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?colId=$COLUMN_ID&timelineId=$TIMELINE_ID"
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
If you already have fresh
|
|
71
|
+
If you already have a fresh grid loaded from Step 3, reading its `cells` array directly (sparse — an entry only exists once something has been placed there) avoids the extra round-trip; use the calls above otherwise, or when the grid might be stale (e.g. a column was just created).
|
|
70
72
|
|
|
71
73
|
## Step 6 — Insert a column at a specific index (conflict resolution)
|
|
72
74
|
|
|
@@ -176,7 +178,7 @@ curl -s -X POST "http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/timeli
|
|
|
176
178
|
|
|
177
179
|
# 2. Fetch chapter to find the target lane cell for the new column
|
|
178
180
|
curl -s -H "x-user-id: place-element-skill" \
|
|
179
|
-
"http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<TIMELINE_ID
|
|
181
|
+
"http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<TIMELINE_ID>?projection=cells"
|
|
180
182
|
|
|
181
183
|
# 3. Create the EVENT node — do not skip the x-user-id header
|
|
182
184
|
curl -s -X POST "http://localhost:3000/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/events" \
|
|
@@ -123,7 +123,7 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>"
|
|
|
123
123
|
|
|
124
124
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 4 — Fetch the chapter's grid state".
|
|
125
125
|
|
|
126
|
-
Parse the result (`{rows, columns, cells}` directly
|
|
126
|
+
Parse the result (`{rows, columns, cells}` directly — the REST fallback's `?projection=cells` returns the same shape):
|
|
127
127
|
- `rows` — list of row objects, each with `id` and `type`
|
|
128
128
|
- `columns` — list of column objects, each with `id`
|
|
129
129
|
- `cells` — list of cell objects, each with `rowId`, `colId`, and optionally `nodeId` (used only to check occupancy)
|
|
@@ -183,7 +183,7 @@ mcp__eventmodelers__create_screen {
|
|
|
183
183
|
"contentType": "html",
|
|
184
184
|
"nodeId": "<SCREEN_NODE_ID>",
|
|
185
185
|
"chapterId": "<CHAPTER_ID>",
|
|
186
|
-
"
|
|
186
|
+
"cellName": "<actorCellName>",
|
|
187
187
|
"title": "<screenTitle>",
|
|
188
188
|
"pages": ["<div>...</div>"],
|
|
189
189
|
"description": "<screenTitle — what this screen shows>"
|
|
@@ -204,7 +204,7 @@ mcp__eventmodelers__create_screen {
|
|
|
204
204
|
"contentType": "sketch",
|
|
205
205
|
"nodeId": "<SCREEN_NODE_ID>",
|
|
206
206
|
"chapterId": "<CHAPTER_ID>",
|
|
207
|
-
"
|
|
207
|
+
"cellName": "<actorCellName>",
|
|
208
208
|
"title": "<screenTitle>",
|
|
209
209
|
"elements": [...],
|
|
210
210
|
"description": "<screenTitle — what this screen shows>"
|
|
@@ -214,7 +214,7 @@ mcp__eventmodelers__create_screen {
|
|
|
214
214
|
|
|
215
215
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 5b — Create the screen node and render it (sketch path, explicit request only)".
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
MCP `create_screen` takes only `cellName`. Don't derive `actorCellName` from array positions in the grid — read it from `get_board_outline` for that chapter (call it once if you don't already hold it): the column letter is the target column's `letter` there, the row number is the target lane's 1-based position in its `lanes` list (lanes are listed top to bottom, so the first lane is row 1) (e.g. column `letter: "D"`, the actor lane first in `lanes` → `D1`). A column appended by `add_column` only has a letter once you re-read the outline. The curl fallback still takes the computed `actorCellId` as `cellId`. Expect success (MCP: `created: true`; curl: `204`). On failure, read the validation error, fix the payload, and retry once before reporting failure.
|
|
218
218
|
|
|
219
219
|
### Step 5b(ii) — Set field data lineage (mandatory)
|
|
220
220
|
|
|
@@ -15,10 +15,10 @@ Extract `id` from the response → `CHAPTER_ID`.
|
|
|
15
15
|
## Step 4 — Fetch the chapter's grid state
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
18
|
+
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID?projection=cells"
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Returns `{rows, columns, cells}` directly (not wrapped in `meta.timelineData`).
|
|
22
22
|
|
|
23
23
|
## Step 5a — Add a column
|
|
24
24
|
|