@eventmodelers/cli 1.0.150 → 1.0.151

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 (23) hide show
  1. package/package.json +1 -1
  2. package/shared/skills/learn-eventmodelers-api/SKILL.md +8 -0
  3. package/stacks/axon/templates/.claude/skills/build-automation/SKILL.md +2 -0
  4. package/stacks/axon/templates/.claude/skills/build-state-change/SKILL.md +2 -0
  5. package/stacks/axon/templates/.claude/skills/build-state-view/SKILL.md +2 -0
  6. package/stacks/classic/templates/.claude/skills/build-automation/SKILL.md +2 -0
  7. package/stacks/classic/templates/.claude/skills/build-state-change/SKILL.md +2 -0
  8. package/stacks/classic/templates/.claude/skills/build-state-view/SKILL.md +2 -0
  9. package/stacks/kurrent/templates/.claude/skills/build-automation/SKILL.md +2 -0
  10. package/stacks/kurrent/templates/.claude/skills/build-state-change/SKILL.md +2 -0
  11. package/stacks/kurrent/templates/.claude/skills/build-state-view/SKILL.md +2 -0
  12. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +18 -0
  13. package/stacks/node/templates/.claude/skills/build-automation/SKILL.md +2 -0
  14. package/stacks/node/templates/.claude/skills/build-state-change/SKILL.md +2 -0
  15. package/stacks/node/templates/.claude/skills/build-state-view/SKILL.md +2 -0
  16. package/stacks/opencqrs/templates/.claude/skills/build-automation/SKILL.md +2 -0
  17. package/stacks/opencqrs/templates/.claude/skills/build-state-change/SKILL.md +2 -0
  18. package/stacks/opencqrs/templates/.claude/skills/build-state-view/SKILL.md +2 -0
  19. package/stacks/supabase/templates/.claude/skills/build-state-change/SKILL.md +2 -0
  20. package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +2 -0
  21. package/stacks/umadb/templates/.claude/skills/build-automation/SKILL.md +2 -0
  22. package/stacks/umadb/templates/.claude/skills/build-state-change/SKILL.md +2 -0
  23. package/stacks/umadb/templates/.claude/skills/build-state-view/SKILL.md +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.150",
3
+ "version": "1.0.151",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -696,6 +696,8 @@ Append Given-When-Then scenario(s) to a column's spec node — the node is creat
696
696
  title: string
697
697
  vertical?: boolean
698
698
  examples?: unknown[]
699
+ context?: SpecStep[] // READMODELs the command decides against
700
+ contextNote?: string // markdown background — ONLY what events, commands and read models can't express
699
701
  given?: SpecStep[] // EVENTs from this timeline
700
702
  when?: SpecStep[] // at most one COMMAND, or one inline QUERY object; empty if then has a READMODEL
701
703
  then?: SpecStep[] // EVENTs only OR exactly one READMODEL (not mixed)
@@ -711,6 +713,7 @@ A step given as `{title, type}` is resolved server-side, so the names you alread
711
713
 
712
714
  **Validation rules**:
713
715
  - `context`: only READMODELs (the state the command decides against)
716
+ - `contextNote`: free markdown, last resort. State belongs in `given` events (or `context` read models), inputs in the `when` command's fields, outcomes in `then` — a note never replaces or repeats any of them. Use it only for background no element can carry: an external condition ("the payment provider is in maintenance"), a business assumption, the reason a case matters. Blank notes are dropped. Exported as `contextNote` on the scenario in slice data
714
717
  - `given`: only EVENTs from same timeline
715
718
  - `when`: max one COMMAND; must be empty when `then` contains a READMODEL (use an inline QUERY item for a state-view)
716
719
  - `then`: all EVENTs OR exactly one READMODEL — never mixed
@@ -765,8 +768,13 @@ Append one or more storylines to a column's spec node. The spec node is auto-cre
765
768
  examples?: unknown[]
766
769
  }>
767
770
  }
771
+
772
+ // A note beat — free markdown anywhere in the sequence, referencing no element:
773
+ { instanceId: 'uuid', refId: '', type: 'STORYLINE_NOTE', title: 'Note', note: 'markdown' }
768
774
  ```
769
775
 
776
+ A note beat is the storyline's `contextNote`: only for what the beats themselves can't express (an external condition, an assumption, a time gap like "two weeks later"). Never use one to stand in for an event, command or read model that should be a beat — model the element instead. It is exported as an element `{id: instanceId, type: 'STORYLINE_NOTE', description: note}`; an empty note is rejected (`STORYLINE_NOTE_EMPTY`).
777
+
770
778
  A beat is addressed either way, like a scenario step:
771
779
  ```typescript
772
780
  { refId: 'node-uuid' } // a board node — always wins, never re-resolved
@@ -396,6 +396,8 @@ class {AutomationName}ProcessorTest {
396
396
 
397
397
  ### Storyline-Derived Tests (Optional)
398
398
 
399
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
400
+
399
401
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs with an ordered
400
402
  `elements[]` "beats" sequence (EVENT/COMMAND/READMODEL/...). This is a secondary, supplementary
401
403
  source; `specifications[]` above stays the primary and default source of test cases. Most slices
@@ -405,6 +405,8 @@ see [references/axon-test-fixture-patterns.md](references/axon-test-fixture-patt
405
405
 
406
406
  ## Step 7b: Storyline-Derived Tests (Optional)
407
407
 
408
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
409
+
408
410
  `slice.json` may carry a `storylines[]` array alongside `specifications[]` — narrated walkthroughs
409
411
  where an ordered sequence of `elements[]` "beats" (EVENT/COMMAND/READMODEL/...) shows one use case
410
412
  end to end. This is a secondary, supplementary source — `specifications[]` (Step 7) stays the
@@ -210,6 +210,8 @@ Do not design your own test cases unless specifically instructed to do so.
210
210
 
211
211
  ## Step 4b: Storyline-Derived Tests (Optional)
212
212
 
213
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
214
+
213
215
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs where the *same* read
214
216
  model appears as multiple ordered "beats" across one flow (see `elements[]` on each storyline).
215
217
  This is a secondary, supplementary source: `specifications[]` (Step 4) remains the primary and
@@ -39,6 +39,8 @@ Follow `/build-state-change` for this slice's command and events — all of its
39
39
 
40
40
  ### Storyline-derived tests
41
41
 
42
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
43
+
42
44
  If `storylines[]` includes a beat sequence running through this automation's trigger → this slice's command, the command test for that segment is already covered by build-state-change's own "Storyline-derived tests" step.
43
45
 
44
46
  ## Step 3 — Find the trigger point
@@ -77,6 +77,8 @@ Name each test after the specification title so it can be traced back to the boa
77
77
 
78
78
  ### Storyline-derived tests (optional)
79
79
 
80
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
81
+
80
82
  `storylines[]` in slice.json (optional — only present when the board author built an explicit walkthrough) can supply additional tests. A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `commands[]` by id/title. Scan each storyline's ordered `elements` for a `type: COMMAND` beat followed by its EVENT beat(s):
81
83
 
82
84
  - `given` — the cumulative ordered events from the start of the storyline up to (not including) the command beat, set up as state as above
@@ -80,6 +80,8 @@ Name each test after the specification title.
80
80
 
81
81
  ### Storyline-derived tests (optional)
82
82
 
83
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
84
+
83
85
  `storylines[]` in slice.json is optional — present only when the board author explicitly built a walkthrough for this flow. When present, mine it for **additional** tests, on top of (never instead of) the specification tests.
84
86
 
85
87
  A storyline is `{ id, title, elements: [...] }`, where `elements` is an ordered list of beats — the same element can repeat to show its state at different points in the flow. A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `readmodels[]` by id/title. Scan for a pair of adjacent beats that are both `type: READMODEL`, with only EVENT beat(s) between them and no COMMAND beat in that run. That pair is one self-contained test:
@@ -384,6 +384,8 @@ class {AutomationName}HandlingTest {
384
384
 
385
385
  ### Storyline-Derived Tests (Optional)
386
386
 
387
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
388
+
387
389
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs with an ordered
388
390
  `elements[]` "beats" sequence (EVENT/COMMAND/READMODEL/...). This is a secondary, supplementary
389
391
  source; `specifications[]` above stays the primary and default source of test cases. Most slices
@@ -376,6 +376,8 @@ the same reconstruction path production uses.
376
376
 
377
377
  ## Step 6b: Storyline-Derived Tests (Optional)
378
378
 
379
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
380
+
379
381
  `slice.json` may carry a `storylines[]` array alongside `specifications[]` — narrated walkthroughs
380
382
  where an ordered sequence of `elements[]` "beats" (EVENT/COMMAND/READMODEL/...) shows one use case
381
383
  end to end. This is a secondary, supplementary source — `specifications[]` (Step 7) stays the
@@ -341,6 +341,8 @@ package — which it already does, following this stack's convention (`{SliceNam
341
341
 
342
342
  ## Step 5b: Storyline-Derived Tests (Optional)
343
343
 
344
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
345
+
344
346
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs where the *same* read
345
347
  model appears as multiple ordered "beats" across one flow (see `elements[]` on each storyline).
346
348
  This is a secondary, supplementary source: `specifications[]` (Step 4) remains the primary and
@@ -50,6 +50,21 @@ narrated lifecycle, GWT covers everything else about that read model.
50
50
  A storyline threads all three beats into one walkthrough; three separate GWTs could each assert one
51
51
  transition but not the lifecycle.
52
52
 
53
+ ## Notes — Last Resort Only
54
+
55
+ A scenario can carry a `contextNote` (markdown), and a storyline can hold note beats
56
+ (`{type: "STORYLINE_NOTE", refId: "", note: "…"}`). **Notes are only for what cannot be expressed
57
+ with events and the other elements.** Before writing one, ask: could this be a `given` event, a
58
+ `context` read model, a field example on the command, or a `then` outcome? If yes, model it that
59
+ way — a scenario that tells its story in a note instead of in events is incomplete, and code
60
+ generated from it can't test what's only in prose.
61
+
62
+ Legit uses: a condition outside the system ("the payment provider is in maintenance"), a business
63
+ assumption or regulation behind the case, why the case matters, a time gap between storyline beats
64
+ ("14 days later"). Not legit: "the customer has already ordered twice" (→ `given` events), "the
65
+ cart total is 120 €" (→ field examples), "an email is sent" (→ a `then` event), restating the
66
+ title. Most scenarios need no note; keep any note to a few lines.
67
+
53
68
  ### Storyline data shape
54
69
 
55
70
  ```json
@@ -77,6 +92,8 @@ transition but not the lifecycle.
77
92
  READMODEL. Beats are only READMODEL or EVENT — no COMMAND, SCREEN or other types. Every `refId`
78
93
  must be on the same timeline.
79
94
  - `isError` marks a beat as an alternate/error branch off the previous beat.
95
+ - A note beat (`type: "STORYLINE_NOTE"`, `refId: ""`, markdown in `note`) may sit anywhere and is
96
+ skipped by the order rules — see "Notes — Last Resort Only" above before adding one.
80
97
  - `fields`, `expectEmptyList`, `exampleMode`, `examples` mirror the same fields GWT scenario steps
81
98
  use, letting a beat show concrete example data the same way a "then" readmodel step does.
82
99
 
@@ -494,6 +511,7 @@ After posting, tell the user:
494
511
  **No command has only 2 scenarios unless all other types were reviewed and found inapplicable.**
495
512
 
496
513
  **Per read model — this is a separate, equally mandatory pass, not an afterthought of the command pass above:**
514
+ - [ ] **Notes carry nothing an element could** — every `contextNote` and note beat holds only background no event, command field or read model can express; anything else is moved into `given`/`when`/`then`/`context` and the note removed
497
515
  - [ ] **Every READMODEL on the board has at least one view scenario** — GWT (`given`: source EVENTs, `when`: empty, `then`: the READMODEL) or a storyline. A model with dozens of command scenarios and 0 read-model scenarios is not a complete Step 7 — it's easy to walk away thinking coverage is thorough because the command side looks exhaustive, so check the read-model side explicitly before reporting this step done.
498
516
  - [ ] **Population scenario** — the view shows correct data after its source event(s)
499
517
  - [ ] **Removal/update scenario, where applicable** — a row disappears or changes (`expectEmptyList: true` for list-type views) after an event that supersedes it (expiry, return, archival, withdrawal, status change, etc.). `EVENT → READMODEL` is exempt from column ordering **only when the read model already feeds an AUTOMATION** (`READMODEL → AUTOMATION` edge; see `learn-eventmodelers-api` §3 and `eventmodeling-orchestrating-event-modeling`'s "No backward arrows") — a later event connecting back to an earlier-placed todo-list read model is normal for that accumulator shape, so add the connection if it's missing rather than assuming the scenario is impossible. If the read model has no automation to feed, the backward connection is rejected — by Step 5 this should already have been modeled the forward way (see `eventmodeling-identifying-outputs` Step 5g's copy pattern: a new read model + screen copy in the later event's column, never a link back). If it wasn't, write the scenario against that forward-placed copy rather than the original. Only skip this scenario, with a documented gap (TASK comment), when the superseding event genuinely lives in a different chapter.
@@ -64,6 +64,8 @@ Refer to the build-state-change skill for the full command handler structure.
64
64
 
65
65
  ### Storyline-derived tests
66
66
 
67
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
68
+
67
69
  If `storylines[]` includes a beat sequence running through this automation's trigger event → this slice's command, the command-handler test for that segment is already covered by build-state-change's own "Storyline-derived tests" step — it runs as part of following that skill above. This skill has no separate reactor-test format: the processor's event-to-command field mapping is exercised only indirectly, through that command-handler test, never by a dedicated `processor.test.ts`.
68
70
 
69
71
  ---
@@ -230,6 +230,8 @@ Add one test per specification in the slice.json. If the spec has no preconditio
230
230
 
231
231
  ### Storyline-derived tests (optional)
232
232
 
233
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
234
+
233
235
  `storylines[]` in slice.json (optional — only present when the board author built an explicit walkthrough) can also supply command-handler tests, in addition to the specifications-derived ones above. A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `commands[]` by id/title. Just scan each storyline's ordered `elements` for a `type: COMMAND` beat, followed by its emitted EVENT beat(s):
234
236
 
235
237
  - `given` — the cumulative ordered events from the start of the storyline up to (not including) the command beat
@@ -327,6 +327,8 @@ Write one `it` block per specification in the slice.json. Use `given([events]).w
327
327
 
328
328
  ### Storyline-derived tests (optional)
329
329
 
330
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
331
+
330
332
  `storylines[]` in slice.json is optional — present only when the board author explicitly built a walkthrough for this flow; most slices have none. When present, mine it for **additional** read-model-chain tests, on top of (never instead of) the specifications-derived tests above.
331
333
 
332
334
  A storyline is `{ id, title, elements: [...] }`, where `elements` is an ordered list of beats — the same element can repeat to show its state at different points in the flow. A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `readmodels[]` by id/title. Just scan for a pair of adjacent beats that are both `type: READMODEL`, with only EVENT beat(s) between them and no COMMAND beat in that run. That pair is one self-contained projection test:
@@ -394,6 +394,8 @@ class {AutomationName}ProcessorTest {
394
394
 
395
395
  ### Storyline-Derived Tests (Optional)
396
396
 
397
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
398
+
397
399
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs with an ordered
398
400
  `elements[]` "beats" sequence (EVENT/COMMAND/READMODEL/...). This is a secondary, supplementary
399
401
  source; `specifications[]` above stays the primary and default source of test cases. Most slices
@@ -368,6 +368,8 @@ Full fluent-API cheat sheet: see
368
368
 
369
369
  ## Step 7b: Storyline-Derived Tests (Optional)
370
370
 
371
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
372
+
371
373
  `slice.json` may carry a `storylines[]` array alongside `specifications[]` — narrated walkthroughs
372
374
  where an ordered sequence of `elements[]` "beats" (EVENT/COMMAND/READMODEL/...) shows one use case
373
375
  end to end. This is a secondary, supplementary source — `specifications[]` (Step 7) stays the
@@ -300,6 +300,8 @@ class {SliceName}ProjectorTest {
300
300
 
301
301
  ## Step 4b: Storyline-Derived Tests (Optional)
302
302
 
303
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
304
+
303
305
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs where the *same* read
304
306
  model appears as multiple ordered "beats" across one flow (see `elements[]` on each storyline).
305
307
  This is a secondary, supplementary source: `specifications[]` (Step 3) remains the primary and
@@ -232,6 +232,8 @@ Add one test per specification in the slice.json. If the spec has no preconditio
232
232
 
233
233
  ## Step 4b — Storyline-derived tests (optional)
234
234
 
235
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
236
+
235
237
  Some slices also have a `storylines[]` array in slice.json — narrated walkthroughs of one use case as an ordered sequence of beats (`elements[]`). Most slices have no storylines; skip this step silently when `storylines[]` is empty or absent.
236
238
 
237
239
  A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `commands[]` by id/title. For each storyline, find a `type: COMMAND` beat directly followed by its EVENT beat(s). That pair is a command-handler test — the same shape as the specifications-derived tests above:
@@ -309,6 +309,8 @@ Write one `it` block per specification in the slice.json. Use `given([events]).w
309
309
 
310
310
  ## Step 5b — Storyline-derived tests (optional)
311
311
 
312
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
313
+
312
314
  Some slices also have a `storylines[]` array in slice.json — narrated walkthroughs where the *same* read model repeats across several beats in one ordered `elements[]` list, showing its state at different points in a single flow. Most slices have no storylines; skip this step silently when `storylines[]` is empty or absent.
313
315
 
314
316
  A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `readmodels[]` by id/title. For each storyline, scan `elements[]` for pairs of adjacent `type: 'READMODEL'` beats with only EVENT beat(s) in between. Each such pair is a clean, self-contained projection test — the same shape as the specifications-derived tests above:
@@ -282,6 +282,8 @@ verifyNoMoreInteractions(targetCommandHandler);
282
282
 
283
283
  ### Storyline-Derived Tests (Optional)
284
284
 
285
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
286
+
285
287
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs with an ordered
286
288
  `elements[]` "beats" sequence (EVENT/COMMAND/READMODEL/...). This is a secondary, supplementary
287
289
  source; `specifications[]` above stays the primary and default source of test cases. Most slices
@@ -344,6 +344,8 @@ behaviour, why `subscribe()` isn't faked: see
344
344
 
345
345
  ## Step 7b: Storyline-Derived Tests (Optional)
346
346
 
347
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
348
+
347
349
  `slice.json` may carry a `storylines[]` array alongside `specifications[]` — narrated walkthroughs
348
350
  where an ordered sequence of `elements[]` "beats" (EVENT/COMMAND/READMODEL/...) shows one use case
349
351
  end to end. This is a secondary, supplementary source — `specifications[]` (Step 7) stays the
@@ -286,6 +286,8 @@ Add a `com.h2database:h2` test-scope dependency once, project-wide, if it isn't
286
286
 
287
287
  ## Step 4b: Storyline-Derived Tests (Optional)
288
288
 
289
+ > **Note beats**: an entry with `type: STORYLINE_NOTE` is a note the board author placed in the walkthrough — its `description` is markdown context (an assumption, an external condition, a time gap), and its `id` is not a board node. Read it as background for the beats around it, but it is never a step: skip it when looking for COMMAND/EVENT/READMODEL beats or judging which beats are adjacent, and never turn it into test setup or an assertion on its own.
290
+
289
291
  `slice.json` may also carry a `storylines[]` array — narrated walkthroughs where the *same* read
290
292
  model appears as multiple ordered "beats" across one flow. This is a secondary, supplementary
291
293
  source: `specifications[]` (Step 4) remains the primary and default source of test cases. Most