@eventmodelers/cli 1.0.89 → 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.
Files changed (59) hide show
  1. package/README.md +2 -0
  2. package/cli.js +26 -2
  3. package/lib/update-check.js +67 -0
  4. package/package.json +4 -1
  5. package/shared/build-kit/lib/adapters/pocketbase-realtime-adapter.js +11 -3
  6. package/shared/build-kit/lib/adapters/realtime-adapter.js +3 -1
  7. package/shared/build-kit/lib/adapters/supabase-realtime-adapter.js +14 -3
  8. package/shared/build-kit/lib/ralph.js +133 -58
  9. package/shared/build-kit/lib/redact.js +36 -0
  10. package/shared/build-kit/lib/turn.js +56 -20
  11. package/shared/build-kit/ralph-claude.js +7 -19
  12. package/shared/build-kit/ralph-exec.js +5 -9
  13. package/shared/build-kit/realtime-agent.js +4 -2
  14. package/shared/skills/connect/SKILL.md +14 -9
  15. package/shared/skills/learn-eventmodelers-api/SKILL.md +60 -21
  16. package/shared/skills/load-slice/SKILL.md +3 -1
  17. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +4 -4
  18. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +18 -9
  19. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +31 -11
  20. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +16 -13
  21. package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +3 -1
  22. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +6 -6
  23. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +3 -1
  24. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +4 -4
  25. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +4 -4
  26. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +8 -12
  27. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +16 -6
  28. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +3 -3
  29. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +1 -1
  30. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +2 -2
  31. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +4 -4
  32. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +4 -4
  33. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +4 -4
  34. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +3 -3
  35. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +3 -1
  36. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +2 -2
  37. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +1 -1
  38. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +3 -3
  39. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +12 -4
  40. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +7 -6
  41. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +4 -3
  42. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +22 -6
  43. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +12 -3
  44. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +18 -6
  45. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +12 -3
  46. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +2 -2
  47. package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +20 -14
  48. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +12 -12
  49. package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +6 -4
  50. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +4 -4
  51. package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +2 -2
  52. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +10 -10
  53. package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +4 -4
  54. package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +11 -24
  55. package/stacks/modeling-kit/templates/kit/AGENTS.md +3 -2
  56. package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +6 -4
  57. package/stacks/modeling-kit/templates/kit/CLAUDE.md +9 -4
  58. package/stacks/react/templates/build-kit/lib/backend-prompt.md +106 -102
  59. 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
  ```
@@ -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
- # → meta.timelineData.columns: [{ id, index }]
23
- # → meta.timelineData.cells: [{ id: "<rowId>-<columnId>", nodeId }]
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
@@ -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 `cellId`, and renders its pages together, so there is no window where the node exists without content:
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
- "cellId": "<actorRowId>-<columnId>",
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 (`meta.timelineData.rows`), not just a grouping in the markdown report** — no two different human roles share the same `actorRowId`
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
@@ -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 — call `mcp__eventmodelers__get_nodes` once per type (no header wiring needed, auth resolves from the connected session):
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
- mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "EVENT" }
34
- mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "COMMAND" }
35
- mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "READMODEL" }
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:** call `get_nodes` once per type, or pull the fuller graph in one shot with `get_slice_data` if you need events/commands/readmodels/screens/specs/actors together:
41
+ **Prefer MCP** — pick the read by what the phase checks:
42
42
 
43
- ```
44
- mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "EVENT" }
45
- mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "COMMAND" }
46
- mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "READMODEL" }
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`) → `mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "chapterId": "$CHAPTER_ID" }` and take the node whose `cellName` is `target` (if multiple chapters exist, find the chapter first with `get_nodes { "type": "CHAPTER" }` and ask the user if ambiguous). Never construct a cell address yourself — only use one you were given or read back as a node's `cellName`.
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, list the board's nodes and match on `meta.title` (the REST nodes endpoint has no name filter):
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
- -H "x-user-id: examples-skill" \
23
- | jq --arg t "$target" '[.[] | select((.meta.title // "") | ascii_downcase | contains($t | ascii_downcase))]'
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`, decode the cell name into a `cellId`:
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` only filters by `type`, not `cellId`:
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`, `meta`, `edges`).
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
- If `TARGET_NODE.edges` is non-empty, fetch each connected node individually by its ID:
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/<EDGE_NODE_ID>" \
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
- Fetch all edge-connected nodes in parallel (one request per node ID).
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 directly via MCP, or from `meta.timelineData` via the REST fallback).
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 `timelineData` already fetched in Step 3 (re-fetch if a column was just created):
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` only filters by `type`. The REST API does support it directly though — `GET /nodes` takes `cellId` **or** `colId`, both requiring `timelineId`:
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 `timelineData` loaded, instead of re-fetching the whole chapter node just to check occupancy.
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
- "cellId": "<CELL_ID>",
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
- "cellId": "<CELL_ID>",
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
- Pass whichever cell reference you already resolved — `CELL_ID` from Step 6, or `CELL_NAME` from Step 1's fast path (either path accepts `cellId` or `cellName`). 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.
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
- Count inbound edges where `target === COMMAND_NODE_ID` and the source node is type `SCREEN` or `AUTOMATION`.
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:
@@ -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 `timelineData` 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 `timelineData` might be stale (e.g. a column was just created).
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 via MCP, or `meta.timelineData` via the REST fallback):
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
- "cellId": "<actorCellId>",
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
- "cellId": "<actorCellId>",
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
- Pass the already-computed `actorCellId` directly as `cellId` in either path. Expect success (MCP: `created: true`; curl: `204`). On failure, read the validation error, fix the payload, and retry once before reporting failure.
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
- Read `meta.timelineData` for `{rows, columns, cells}`.
21
+ Returns `{rows, columns, cells}` directly (not wrapped in `meta.timelineData`).
22
22
 
23
23
  ## Step 5a — Add a column
24
24