@eventmodelers/cli 1.0.71 → 1.0.73

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 (28) hide show
  1. package/README.md +25 -8
  2. package/RELEASE_NOTES.md +5 -0
  3. package/cli.js +211 -49
  4. package/package.json +1 -1
  5. package/shared/demo-slices/Understanding Eventsourcing/additem/slice.json +176 -0
  6. package/shared/demo-slices/Understanding Eventsourcing/archiveitem/slice.json +209 -0
  7. package/shared/demo-slices/Understanding Eventsourcing/cartitems/slice.json +236 -0
  8. package/shared/demo-slices/Understanding Eventsourcing/cartpublicationfailed/slice.json +65 -0
  9. package/shared/demo-slices/Understanding Eventsourcing/cartpublished/slice.json +65 -0
  10. package/shared/demo-slices/Understanding Eventsourcing/cartswithproducts/slice.json +105 -0
  11. package/shared/demo-slices/Understanding Eventsourcing/changedprices/slice.json +91 -0
  12. package/shared/demo-slices/Understanding Eventsourcing/changeinventory/slice.json +191 -0
  13. package/shared/demo-slices/Understanding Eventsourcing/changeprice/slice.json +220 -0
  14. package/shared/demo-slices/Understanding Eventsourcing/clearcart/slice.json +219 -0
  15. package/shared/demo-slices/Understanding Eventsourcing/config.json +2631 -0
  16. package/shared/demo-slices/Understanding Eventsourcing/context.json +3 -0
  17. package/shared/demo-slices/Understanding Eventsourcing/index.json +132 -0
  18. package/shared/demo-slices/Understanding Eventsourcing/inventories/slice.json +132 -0
  19. package/shared/demo-slices/Understanding Eventsourcing/itemadded/slice.json +137 -0
  20. package/shared/demo-slices/Understanding Eventsourcing/publishcart/slice.json +230 -0
  21. package/shared/demo-slices/Understanding Eventsourcing/removeitem/slice.json +239 -0
  22. package/shared/demo-slices/Understanding Eventsourcing/submitcart/slice.json +199 -0
  23. package/shared/demo-slices/Understanding Eventsourcing/submittedcartdata/slice.json +90 -0
  24. package/shared/demo-slices/current_context.json +3 -0
  25. package/shared/skills/learn-eventmodelers-api/SKILL.md +1 -1
  26. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -0
  27. package/stacks/modeling-kit/templates/kit/CLAUDE-STANDALONE.md +14 -9
  28. package/stacks/modeling-kit/templates/kit/CLAUDE.md +12 -7
@@ -0,0 +1,239 @@
1
+ {
2
+ "id": "f263e451-78a6-475e-a112-bff181295e65",
3
+ "status": "Planned",
4
+ "title": "Remove Item",
5
+ "context": "Understanding Eventsourcing",
6
+ "sliceType": "STATE_CHANGE",
7
+ "commands": [
8
+ {
9
+ "id": "f04eb51d-6afd-4a33-8e3d-941dea5a3abc",
10
+ "tags": [],
11
+ "lane": "Interaction",
12
+ "modelContext": "Understanding Eventsourcing",
13
+ "slice": "Remove Item",
14
+ "title": "Remove Item",
15
+ "fields": [
16
+ {
17
+ "name": "aggregateId",
18
+ "type": "UUID",
19
+ "example": "",
20
+ "mapping": "",
21
+ "optional": false,
22
+ "subfields": [],
23
+ "cardinality": "Single",
24
+ "idAttribute": false
25
+ },
26
+ {
27
+ "name": "itemId",
28
+ "type": "UUID",
29
+ "example": "",
30
+ "mapping": "",
31
+ "optional": false,
32
+ "subfields": [],
33
+ "cardinality": "Single",
34
+ "idAttribute": false
35
+ }
36
+ ],
37
+ "type": "COMMAND",
38
+ "description": "",
39
+ "aggregate": "default",
40
+ "aggregateDependencies": [],
41
+ "dependencies": [
42
+ {
43
+ "id": "b5b7ae0a-d0a7-4b2b-bc14-2086993eface",
44
+ "type": "OUTBOUND",
45
+ "title": "Item Removed",
46
+ "elementType": "EVENT"
47
+ },
48
+ {
49
+ "id": "9e7ae82a-42f0-4298-85f4-fc2f0f689f43",
50
+ "type": "INBOUND",
51
+ "title": "title",
52
+ "elementType": "SCREEN"
53
+ }
54
+ ],
55
+ "apiEndpoint": "",
56
+ "createsAggregate": false,
57
+ "triggers": [],
58
+ "elementCopy": false,
59
+ "linkedId": "f04eb51d-6afd-4a33-8e3d-941dea5a3abc",
60
+ "sketched": false,
61
+ "prototype": {
62
+ "activeByDefault": false
63
+ },
64
+ "comments": []
65
+ }
66
+ ],
67
+ "events": [
68
+ {
69
+ "id": "b5b7ae0a-d0a7-4b2b-bc14-2086993eface",
70
+ "tags": [],
71
+ "lane": "Events",
72
+ "modelContext": "Understanding Eventsourcing",
73
+ "slice": "Remove Item",
74
+ "title": "Item Removed",
75
+ "fields": [
76
+ {
77
+ "name": "aggregateId",
78
+ "type": "UUID",
79
+ "example": "",
80
+ "mapping": "",
81
+ "optional": false,
82
+ "subfields": [],
83
+ "cardinality": "Single",
84
+ "idAttribute": false
85
+ },
86
+ {
87
+ "name": "itemId",
88
+ "type": "UUID",
89
+ "example": "",
90
+ "mapping": "",
91
+ "optional": false,
92
+ "subfields": [],
93
+ "cardinality": "Single",
94
+ "idAttribute": false
95
+ }
96
+ ],
97
+ "type": "EVENT",
98
+ "description": "",
99
+ "aggregate": "default",
100
+ "aggregateDependencies": [],
101
+ "dependencies": [
102
+ {
103
+ "id": "22511305-2296-43ba-b8e8-d786d53cd25f",
104
+ "type": "OUTBOUND",
105
+ "title": "Carts with Products",
106
+ "elementType": "READMODEL"
107
+ },
108
+ {
109
+ "id": "631c5aa8-0f9e-4a8f-b7f6-91174cae2b82",
110
+ "type": "OUTBOUND",
111
+ "title": "cart items",
112
+ "elementType": "READMODEL"
113
+ },
114
+ {
115
+ "id": "f04eb51d-6afd-4a33-8e3d-941dea5a3abc",
116
+ "type": "INBOUND",
117
+ "title": "Remove Item",
118
+ "elementType": "COMMAND"
119
+ }
120
+ ],
121
+ "apiEndpoint": "",
122
+ "createsAggregate": false,
123
+ "triggers": [],
124
+ "elementCopy": false,
125
+ "linkedId": "b5b7ae0a-d0a7-4b2b-bc14-2086993eface",
126
+ "sketched": false,
127
+ "prototype": {
128
+ "activeByDefault": false
129
+ },
130
+ "comments": []
131
+ }
132
+ ],
133
+ "readmodels": [],
134
+ "screens": [
135
+ {
136
+ "id": "9e7ae82a-42f0-4298-85f4-fc2f0f689f43",
137
+ "tags": [],
138
+ "lane": "Actor",
139
+ "modelContext": "Understanding Eventsourcing",
140
+ "slice": "Remove Item",
141
+ "title": "title",
142
+ "fields": [
143
+ {
144
+ "name": "aggregateId",
145
+ "type": "UUID",
146
+ "example": "",
147
+ "mapping": "",
148
+ "optional": false,
149
+ "cardinality": "Single",
150
+ "idAttribute": false
151
+ },
152
+ {
153
+ "name": "description",
154
+ "type": "String",
155
+ "example": "",
156
+ "mapping": "",
157
+ "optional": false,
158
+ "cardinality": "Single",
159
+ "idAttribute": false
160
+ },
161
+ {
162
+ "name": "image",
163
+ "type": "String",
164
+ "example": "",
165
+ "mapping": "",
166
+ "optional": false,
167
+ "cardinality": "Single",
168
+ "idAttribute": false
169
+ },
170
+ {
171
+ "name": "itemId",
172
+ "type": "UUID",
173
+ "example": "",
174
+ "mapping": "",
175
+ "optional": false,
176
+ "cardinality": "Single",
177
+ "idAttribute": false
178
+ },
179
+ {
180
+ "name": "price",
181
+ "type": "Double",
182
+ "example": "",
183
+ "mapping": "",
184
+ "optional": false,
185
+ "cardinality": "Single",
186
+ "idAttribute": false
187
+ },
188
+ {
189
+ "name": "totalPrice",
190
+ "type": "Double",
191
+ "example": "",
192
+ "mapping": "",
193
+ "optional": false,
194
+ "cardinality": "Single",
195
+ "idAttribute": false
196
+ }
197
+ ],
198
+ "type": "SCREEN",
199
+ "description": "",
200
+ "aggregate": "default",
201
+ "aggregateDependencies": [],
202
+ "dependencies": [
203
+ {
204
+ "id": "f04eb51d-6afd-4a33-8e3d-941dea5a3abc",
205
+ "type": "OUTBOUND",
206
+ "title": "Remove Item",
207
+ "elementType": "COMMAND"
208
+ },
209
+ {
210
+ "id": "631c5aa8-0f9e-4a8f-b7f6-91174cae2b82",
211
+ "type": "INBOUND",
212
+ "title": "cart items",
213
+ "elementType": "READMODEL"
214
+ }
215
+ ],
216
+ "apiEndpoint": "",
217
+ "createsAggregate": false,
218
+ "triggers": [],
219
+ "elementCopy": false,
220
+ "linkedId": "9e7ae82a-42f0-4298-85f4-fc2f0f689f43",
221
+ "sketched": false,
222
+ "prototype": {
223
+ "activeByDefault": false
224
+ },
225
+ "comments": []
226
+ }
227
+ ],
228
+ "screenImages": [],
229
+ "screenLayouts": [],
230
+ "processors": [],
231
+ "tables": [],
232
+ "specifications": [],
233
+ "storylines": [],
234
+ "chapter": "Understanding Eventsourcing",
235
+ "codeGen": {},
236
+ "notes": [],
237
+ "comments": [],
238
+ "aggregates": []
239
+ }
@@ -0,0 +1,199 @@
1
+ {
2
+ "id": "46e2eee1-54ef-4a9c-9403-a3403bf45943",
3
+ "status": "Planned",
4
+ "title": "Submit Cart",
5
+ "context": "Understanding Eventsourcing",
6
+ "sliceType": "STATE_CHANGE",
7
+ "commands": [
8
+ {
9
+ "id": "af612742-5aba-4d05-950a-fae77c5f6307",
10
+ "tags": [],
11
+ "lane": "Interaction",
12
+ "modelContext": "Understanding Eventsourcing",
13
+ "slice": "Submit Cart",
14
+ "title": "Submit Cart",
15
+ "fields": [
16
+ {
17
+ "name": "aggregateId",
18
+ "type": "UUID",
19
+ "example": "",
20
+ "mapping": "",
21
+ "optional": false,
22
+ "subfields": [],
23
+ "cardinality": "Single",
24
+ "idAttribute": false
25
+ },
26
+ {
27
+ "name": "orderedProducts",
28
+ "type": "Custom",
29
+ "example": "",
30
+ "mapping": "",
31
+ "optional": false,
32
+ "cardinality": "List",
33
+ "idAttribute": false
34
+ }
35
+ ],
36
+ "type": "COMMAND",
37
+ "description": "",
38
+ "aggregate": "default",
39
+ "aggregateDependencies": [],
40
+ "dependencies": [
41
+ {
42
+ "id": "29ae25b9-56cd-4086-bbfe-a64efb30d240",
43
+ "type": "OUTBOUND",
44
+ "title": "Cart Submitted",
45
+ "elementType": "EVENT"
46
+ },
47
+ {
48
+ "id": "0e77e668-00df-404a-9334-3f22153c7ab6",
49
+ "type": "INBOUND",
50
+ "title": "title",
51
+ "elementType": "SCREEN"
52
+ }
53
+ ],
54
+ "apiEndpoint": "",
55
+ "createsAggregate": false,
56
+ "triggers": [],
57
+ "elementCopy": false,
58
+ "linkedId": "af612742-5aba-4d05-950a-fae77c5f6307",
59
+ "sketched": false,
60
+ "prototype": {
61
+ "activeByDefault": false
62
+ },
63
+ "comments": []
64
+ }
65
+ ],
66
+ "events": [
67
+ {
68
+ "id": "29ae25b9-56cd-4086-bbfe-a64efb30d240",
69
+ "tags": [],
70
+ "lane": "Events",
71
+ "modelContext": "Understanding Eventsourcing",
72
+ "slice": "Submit Cart",
73
+ "title": "Cart Submitted",
74
+ "fields": [
75
+ {
76
+ "name": "aggregateId",
77
+ "type": "UUID",
78
+ "example": "",
79
+ "mapping": "",
80
+ "optional": false,
81
+ "subfields": [],
82
+ "cardinality": "Single",
83
+ "idAttribute": false
84
+ },
85
+ {
86
+ "name": "orderedProducts",
87
+ "type": "Custom",
88
+ "example": "productId, price",
89
+ "mapping": "",
90
+ "optional": false,
91
+ "cardinality": "List",
92
+ "idAttribute": false
93
+ },
94
+ {
95
+ "name": "totalPrice",
96
+ "type": "Double",
97
+ "example": "",
98
+ "mapping": "orderedProducts",
99
+ "optional": false,
100
+ "subfields": [],
101
+ "cardinality": "Single",
102
+ "idAttribute": false
103
+ }
104
+ ],
105
+ "type": "EVENT",
106
+ "description": "",
107
+ "aggregate": "default",
108
+ "aggregateDependencies": [],
109
+ "dependencies": [
110
+ {
111
+ "id": "e3dc2c7e-7327-480a-bdb4-4b2055327540",
112
+ "type": "OUTBOUND",
113
+ "title": "Submitted Cart Data",
114
+ "elementType": "READMODEL"
115
+ },
116
+ {
117
+ "id": "af612742-5aba-4d05-950a-fae77c5f6307",
118
+ "type": "INBOUND",
119
+ "title": "Submit Cart",
120
+ "elementType": "COMMAND"
121
+ }
122
+ ],
123
+ "apiEndpoint": "",
124
+ "createsAggregate": false,
125
+ "triggers": [],
126
+ "elementCopy": false,
127
+ "linkedId": "29ae25b9-56cd-4086-bbfe-a64efb30d240",
128
+ "sketched": false,
129
+ "prototype": {
130
+ "activeByDefault": false
131
+ },
132
+ "comments": []
133
+ }
134
+ ],
135
+ "readmodels": [],
136
+ "screens": [
137
+ {
138
+ "id": "0e77e668-00df-404a-9334-3f22153c7ab6",
139
+ "tags": [],
140
+ "lane": "Actor",
141
+ "modelContext": "Understanding Eventsourcing",
142
+ "slice": "Submit Cart",
143
+ "title": "title",
144
+ "fields": [
145
+ {
146
+ "name": "aggregateId",
147
+ "type": "UUID",
148
+ "example": "",
149
+ "mapping": "",
150
+ "optional": false,
151
+ "cardinality": "Single",
152
+ "idAttribute": false
153
+ },
154
+ {
155
+ "name": "orderedProducts",
156
+ "type": "Custom",
157
+ "example": "{\"productid\":UUID, \"price\":Double}",
158
+ "mapping": "",
159
+ "optional": false,
160
+ "cardinality": "Single",
161
+ "idAttribute": false
162
+ }
163
+ ],
164
+ "type": "SCREEN",
165
+ "description": "",
166
+ "aggregate": "default",
167
+ "aggregateDependencies": [],
168
+ "dependencies": [
169
+ {
170
+ "id": "af612742-5aba-4d05-950a-fae77c5f6307",
171
+ "type": "OUTBOUND",
172
+ "title": "Submit Cart",
173
+ "elementType": "COMMAND"
174
+ }
175
+ ],
176
+ "apiEndpoint": "",
177
+ "createsAggregate": false,
178
+ "triggers": [],
179
+ "elementCopy": false,
180
+ "linkedId": "0e77e668-00df-404a-9334-3f22153c7ab6",
181
+ "sketched": false,
182
+ "prototype": {
183
+ "activeByDefault": false
184
+ },
185
+ "comments": []
186
+ }
187
+ ],
188
+ "screenImages": [],
189
+ "screenLayouts": [],
190
+ "processors": [],
191
+ "tables": [],
192
+ "specifications": [],
193
+ "storylines": [],
194
+ "chapter": "Understanding Eventsourcing",
195
+ "codeGen": {},
196
+ "notes": [],
197
+ "comments": [],
198
+ "aggregates": []
199
+ }
@@ -0,0 +1,90 @@
1
+ {
2
+ "id": "412dfce9-0843-4ac0-82e5-4e645716d502",
3
+ "status": "Planned",
4
+ "title": "Submitted Cart Data",
5
+ "context": "Understanding Eventsourcing",
6
+ "sliceType": "STATE_VIEW",
7
+ "commands": [],
8
+ "events": [],
9
+ "readmodels": [
10
+ {
11
+ "id": "e3dc2c7e-7327-480a-bdb4-4b2055327540",
12
+ "tags": [],
13
+ "lane": "Interaction",
14
+ "modelContext": "Understanding Eventsourcing",
15
+ "slice": "Submitted Cart Data",
16
+ "title": "Submitted Cart Data",
17
+ "fields": [
18
+ {
19
+ "name": "aggregateId",
20
+ "type": "UUID",
21
+ "example": "",
22
+ "mapping": "",
23
+ "optional": false,
24
+ "subfields": [],
25
+ "cardinality": "Single",
26
+ "idAttribute": false
27
+ },
28
+ {
29
+ "name": "orderedProducts",
30
+ "type": "Custom",
31
+ "example": "productId, price",
32
+ "mapping": "",
33
+ "optional": false,
34
+ "cardinality": "List",
35
+ "idAttribute": false
36
+ },
37
+ {
38
+ "name": "totalPrice",
39
+ "type": "Double",
40
+ "example": "",
41
+ "mapping": "orderedProducts",
42
+ "optional": false,
43
+ "subfields": [],
44
+ "cardinality": "Single",
45
+ "idAttribute": false
46
+ }
47
+ ],
48
+ "type": "READMODEL",
49
+ "description": "",
50
+ "aggregate": "default",
51
+ "aggregateDependencies": [],
52
+ "dependencies": [
53
+ {
54
+ "id": "e3073105-b883-444b-b7d2-170de493e093",
55
+ "type": "OUTBOUND",
56
+ "title": "automation",
57
+ "elementType": "AUTOMATION"
58
+ },
59
+ {
60
+ "id": "29ae25b9-56cd-4086-bbfe-a64efb30d240",
61
+ "type": "INBOUND",
62
+ "title": "Cart Submitted",
63
+ "elementType": "EVENT"
64
+ }
65
+ ],
66
+ "apiEndpoint": "",
67
+ "createsAggregate": false,
68
+ "triggers": [],
69
+ "elementCopy": false,
70
+ "linkedId": "e3dc2c7e-7327-480a-bdb4-4b2055327540",
71
+ "sketched": false,
72
+ "prototype": {
73
+ "activeByDefault": false
74
+ },
75
+ "comments": []
76
+ }
77
+ ],
78
+ "screens": [],
79
+ "screenImages": [],
80
+ "screenLayouts": [],
81
+ "processors": [],
82
+ "tables": [],
83
+ "specifications": [],
84
+ "storylines": [],
85
+ "chapter": "Understanding Eventsourcing",
86
+ "codeGen": {},
87
+ "notes": [],
88
+ "comments": [],
89
+ "aggregates": []
90
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "name": "Understanding Eventsourcing"
3
+ }
@@ -881,7 +881,7 @@ Set a prompt's status, optionally attaching a progress comment. Auth: `x-token`
881
881
  ```
882
882
 
883
883
  **Response**: `200` — the updated row
884
- **Errors**: `400` invalid/missing `status` · `403` token not for this prompt's org · `404` prompt not found
884
+ **Errors**: `400` invalid/missing `status` · `403` token not for this prompt's org · `404` prompt not found · `409` (`PROMPT_STATUS_ALREADY_SET`) the prompt already carries that status — another agent already made this transition, so don't retry it
885
885
 
886
886
  ---
887
887
 
@@ -52,6 +52,7 @@ Omit `comment` entirely when there isn't one — don't pass an empty string.
52
52
  |----------|---------|--------|
53
53
  | `400` | `status` missing or not a valid value | Fix the value and retry — do not retry with the same bad value. |
54
54
  | `404` | Prompt not found | The prompt may have been deleted by its author while you were working. Report this and move on — do not treat it as a failure of your actual task work. |
55
+ | `409` | The prompt already carries the status you asked for (`PROMPT_STATUS_ALREADY_SET`) | Somebody else — usually another agent working the same queue — already made this transition. Do not retry it; treat the transition as done and carry on. |
55
56
  | `401`/`403` | Token invalid or wrong organization | Re-run `connect` to refresh credentials, then retry once. |
56
57
 
57
58
  ---
@@ -14,11 +14,11 @@ Format — stays in `.agent-modeling-kit/CLAUDE.md` and still applies.
14
14
  These turns come in two shapes, and both are self-directed — nobody asked you for anything:
15
15
 
16
16
  ```
17
- BOARD_CHANGE board_id=<uuid> organization_id=<uuid> seq=118..124 events=9 nodes=3
17
+ BOARD_CHANGE board_id=<uuid> organization_id=<uuid> seq=118..124 events=7 nodes=3
18
18
  changed:
19
19
  - 9f3c…: node:created, node:changed (4×)
20
20
  - a12b…: node:changed (2×) — possibly your own earlier write
21
- - c771…: edge:added (3×)
21
+ - c771…: node:deleted
22
22
  ```
23
23
 
24
24
  ```
@@ -44,10 +44,12 @@ Steps:
44
44
  1. **Get the whole picture, not just the changed nodes — in two reads, not twenty.** The
45
45
  whole `changed:` list goes into **one**
46
46
  `mcp__eventmodelers__get_nodes { boardId, nodeIds: [...] }` (or the REST equivalent), and
47
- the area around it into **one** `get_board_outline` per chapter; `projection: "line"` is
48
- enough for both whenever you only need names, types and slice statuses. That pair is your
49
- orientation — and the outline half of it you already have from the `SESSION_START` warm-up, so
50
- re-read a chapter only where this turn's `changed:` list says it moved on. Widen out from it to what the nodes sit in — their cell, their slice, the
47
+ the area around it into **one** `get_board_outline` per chapter those nodes land in; `projection: "line"`
48
+ is enough for both whenever you only need names, types and slice statuses. That pair is your
49
+ orientation — and you pay for a chapter's half of it **once per session**: the `SESSION_START` warm-up
50
+ deliberately read no chapter at all, so the first turn that touches one fetches its outline and keeps it,
51
+ and every later turn works from that copy, re-reading only where this turn's `changed:` list says it moved
52
+ on. A chapter nothing has changed in is a chapter you never read. Widen out from it to what the nodes sit in — their cell, their slice, the
51
53
  chain they belong to, the timeline around them — and spend a full-`meta` `get_nodes` only
52
54
  on the handful you conclude you are actually going to touch. One `get_node` per changed
53
55
  node, or a second outline call for a chapter you already read this turn, is the same
@@ -199,6 +201,9 @@ Steps:
199
201
 
200
202
  Keep these turns finished within the turn: wait for the subagents you dispatched, don't leave
201
203
  work trailing. Everything you and they write to the board comes back on this same channel as
202
- another change; the CLI labels changes that arrive in that echo window rather than dropping
203
- them, so you'll see your own writes listed on a later turn recognize them and don't rework
204
- them.
204
+ another change. A burst that is *only* your own writes never becomes a turn at all, so a
205
+ change list you are handed always contains something that isn't yoursbut it may still list
206
+ yours alongside it, marked `YOUR OWN earlier write`. Take that mark literally: those lines are
207
+ there for context, not to be reworked. A line marked `unattributed, possibly your own earlier
208
+ write` is the one uncertain case (a write that reached the platform without an agent id);
209
+ anything unmarked was written by someone else and is real work to look at.
@@ -47,10 +47,13 @@ On that turn, and only that turn:
47
47
  2. Run `/connect` with the header's `token=`/`org=`/`baseUrl=` and the turn's `board_id`. This
48
48
  is the session's one-time connect; step 2 below then applies unchanged, which means no later
49
49
  turn runs `/connect` again unless the board changed or an API call came back `401`/`403`.
50
- 3. Do the orientation read — one `get_board_outline` per chapter, or `get_nodes` with
51
- `projection: "line"` and **keep it**. That is this session's board picture: chapters,
52
- columns, elements, slice statuses. Later turns start from it instead of re-reading the board,
53
- and refresh it when a turn's own changes invalidate it.
50
+ 3. Learn **which chapters exist** — one `get_chapter_bounds` gives every chapter's id and title —
51
+ and stop there. Do **not** read any chapter's contents on this turn: a board of a dozen chapters
52
+ read up front is a minute and a dollar spent before anyone has asked for anything, and most of
53
+ what comes back is never used. A chapter is read on the first turn that has business in it (one
54
+ `get_board_outline`, or `get_nodes` with `projection: "line"`) and **kept** from then on — that
55
+ growing set of read chapters is this session's board picture: columns, elements, slice statuses.
56
+ Later turns answer from it instead of re-reading, and refresh only the part a change invalidated.
54
57
 
55
58
  It is not a prompt turn and not a self-directed one: there is no `prompt_id` (so no
56
59
  `/update-prompt-status` — the "exactly two calls per turn" rule is about prompt turns), nothing
@@ -85,9 +88,11 @@ why; if all of it is, change nothing and post a `COMMENT` on that slice saying w
85
88
  blocked it.
86
89
 
87
90
  **One board read, shared by the whole turn.** Orientation first — `get_board_outline`, or `get_nodes` with
88
- `projection: "line"` — to establish where the work actually is. In a `standalone=on` session you already hold
89
- that orientation from the `SESSION_START` warm-up, so use it rather than re-fetching it, and refresh it only
90
- when this turn's own changes (or a change you were notified of) have made it stale. Then a single full-`meta` `get_nodes`, scoped by
91
+ `projection: "line"` — to establish where the work actually is, and only for the chapter this turn is about.
92
+ In a `standalone=on` session the `SESSION_START` warm-up gave you the chapter list but no chapter's contents,
93
+ so the first turn to touch a chapter pays for its outline once and every later turn in the session reads it
94
+ from memory; refresh only when this turn's own changes (or a change you were notified of) have made your copy
95
+ stale. Then a single full-`meta` `get_nodes`, scoped by
91
96
  `chapterId` or `nodeIds`, covering the nodes you concluded you will touch. Both tiers are once per turn: keep what
92
97
  came back and answer later questions from it instead of re-fetching a chapter you already hold. `/connect` Step 5
93
98
  carries the full discipline — the two tiers, the one-call `submit_node_events` rule for writes, and the per-turn