@milehimikey/em 1.20.0 → 1.22.0

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 (208) hide show
  1. package/.claude/agents/em-implementer.md +10 -0
  2. package/.claude/agents/em-validator.md +1 -0
  3. package/.claude/skills/event-modeling/SKILL.md +1 -1
  4. package/.claude/skills/event-modeling-conform/SKILL.md +7 -4
  5. package/.claude/skills/event-modeling-conform/reference/conform.md +82 -4
  6. package/.claude/skills/event-modeling-design/SKILL.md +23 -8
  7. package/.claude/skills/event-modeling-discover/SKILL.md +2 -2
  8. package/.claude/skills/event-modeling-discover/reference/extract.md +5 -1
  9. package/.claude/skills/event-modeling-engagement/SKILL.md +1 -1
  10. package/.claude/skills/event-modeling-engagement/reference/engagement.md +1 -0
  11. package/.claude/skills/event-modeling-implement/SKILL.md +5 -3
  12. package/.claude/skills/event-modeling-implement/reference/implement.md +14 -0
  13. package/.claude/skills/event-modeling-review/SKILL.md +19 -7
  14. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +54 -9
  15. package/.claude/skills/event-modeling-shared/reference/methodology.md +35 -2
  16. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +26 -15
  17. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +2 -1
  18. package/.claude/skills/event-modeling-shared/templates/model-readme.md +10 -2
  19. package/.claude/skills/event-modeling-shared/templates/slice.md +7 -1
  20. package/.claude-plugin/marketplace.json +2 -2
  21. package/dist/catalog/build.js +6 -5
  22. package/dist/catalog/build.js.map +1 -1
  23. package/dist/catalog/coverage.d.ts +3 -0
  24. package/dist/catalog/coverage.js +12 -0
  25. package/dist/catalog/coverage.js.map +1 -1
  26. package/dist/catalog/docLocate.d.ts +17 -0
  27. package/dist/catalog/docLocate.js +97 -0
  28. package/dist/catalog/docLocate.js.map +1 -0
  29. package/dist/catalog/frontmatterCoherenceValidate.js +16 -1
  30. package/dist/catalog/frontmatterCoherenceValidate.js.map +1 -1
  31. package/dist/catalog/orphanedSliceDocValidate.js +15 -10
  32. package/dist/catalog/orphanedSliceDocValidate.js.map +1 -1
  33. package/dist/catalog/readSliceDoc.js +5 -4
  34. package/dist/catalog/readSliceDoc.js.map +1 -1
  35. package/dist/catalog/scenarioQualityValidate.js +2 -2
  36. package/dist/catalog/scenarioQualityValidate.js.map +1 -1
  37. package/dist/catalog/sliceDoc.d.ts +8 -0
  38. package/dist/catalog/sliceDoc.js +9 -0
  39. package/dist/catalog/sliceDoc.js.map +1 -1
  40. package/dist/catalog/sliceSections.d.ts +18 -1
  41. package/dist/catalog/sliceSections.js +38 -4
  42. package/dist/catalog/sliceSections.js.map +1 -1
  43. package/dist/cli/api.d.ts +84 -0
  44. package/dist/cli/api.js +146 -15
  45. package/dist/cli/api.js.map +1 -1
  46. package/dist/cli/bindings.d.ts +152 -0
  47. package/dist/cli/bindings.js +462 -0
  48. package/dist/cli/bindings.js.map +1 -0
  49. package/dist/cli/conformScope.d.ts +32 -0
  50. package/dist/cli/conformScope.js +68 -5
  51. package/dist/cli/conformScope.js.map +1 -1
  52. package/dist/cli/defer.js +3 -2
  53. package/dist/cli/defer.js.map +1 -1
  54. package/dist/cli/engagement.d.ts +24 -1
  55. package/dist/cli/engagement.js +50 -6
  56. package/dist/cli/engagement.js.map +1 -1
  57. package/dist/cli/engagementBrief.d.ts +127 -0
  58. package/dist/cli/engagementBrief.js +313 -0
  59. package/dist/cli/engagementBrief.js.map +1 -0
  60. package/dist/cli/engagementFile.d.ts +14 -0
  61. package/dist/cli/engagementFile.js +54 -0
  62. package/dist/cli/engagementFile.js.map +1 -1
  63. package/dist/cli/ledgerCheck.js +11 -8
  64. package/dist/cli/ledgerCheck.js.map +1 -1
  65. package/dist/cli/markImplemented.js +2 -2
  66. package/dist/cli/markImplemented.js.map +1 -1
  67. package/dist/cli/ratify.js +2 -2
  68. package/dist/cli/ratify.js.map +1 -1
  69. package/dist/cli/refsCheck.js +3 -2
  70. package/dist/cli/refsCheck.js.map +1 -1
  71. package/dist/cli/render-inputs.d.ts +1 -1
  72. package/dist/cli/render-inputs.js +3 -2
  73. package/dist/cli/render-inputs.js.map +1 -1
  74. package/dist/cli/review.js +2 -2
  75. package/dist/cli/review.js.map +1 -1
  76. package/dist/cli/revise.js +2 -2
  77. package/dist/cli/revise.js.map +1 -1
  78. package/dist/cli/skillDirs.d.ts +22 -0
  79. package/dist/cli/skillDirs.js +24 -0
  80. package/dist/cli/skillDirs.js.map +1 -1
  81. package/dist/cli/sliceConform.js +2 -1
  82. package/dist/cli/sliceConform.js.map +1 -1
  83. package/dist/cli/sliceIndex.d.ts +19 -3
  84. package/dist/cli/sliceIndex.js +81 -17
  85. package/dist/cli/sliceIndex.js.map +1 -1
  86. package/dist/cli/sliceNew.js +2 -0
  87. package/dist/cli/sliceNew.js.map +1 -1
  88. package/dist/cli/sliceStubAll.js +7 -2
  89. package/dist/cli/sliceStubAll.js.map +1 -1
  90. package/dist/cli/sliceSync.d.ts +12 -0
  91. package/dist/cli/sliceSync.js +42 -4
  92. package/dist/cli/sliceSync.js.map +1 -1
  93. package/dist/cli/status.d.ts +8 -4
  94. package/dist/cli/status.js +6 -4
  95. package/dist/cli/status.js.map +1 -1
  96. package/dist/cli/storylineSplit.d.ts +18 -0
  97. package/dist/cli/storylineSplit.js +110 -0
  98. package/dist/cli/storylineSplit.js.map +1 -0
  99. package/dist/cli/upgrade.js +47 -23
  100. package/dist/cli/upgrade.js.map +1 -1
  101. package/dist/cli/watchStoryline.d.ts +6 -0
  102. package/dist/cli/watchStoryline.js +10 -0
  103. package/dist/cli/watchStoryline.js.map +1 -1
  104. package/dist/cli.js +408 -78
  105. package/dist/cli.js.map +1 -1
  106. package/dist/emit/api.d.ts +8 -1
  107. package/dist/emit/api.js +68 -24
  108. package/dist/emit/api.js.map +1 -1
  109. package/dist/emit/apiSurfaceJson.d.ts +3 -0
  110. package/dist/emit/apiSurfaceJson.js +26 -0
  111. package/dist/emit/apiSurfaceJson.js.map +1 -0
  112. package/dist/emit/engagementJson.d.ts +8 -3
  113. package/dist/emit/engagementJson.js +31 -4
  114. package/dist/emit/engagementJson.js.map +1 -1
  115. package/dist/emit/json.d.ts +22 -1
  116. package/dist/emit/json.js +12 -1
  117. package/dist/emit/json.js.map +1 -1
  118. package/dist/mcp/server.js +111 -9
  119. package/dist/mcp/server.js.map +1 -1
  120. package/dist/model/edges.d.ts +1 -1
  121. package/dist/model/edges.js +8 -1
  122. package/dist/model/edges.js.map +1 -1
  123. package/dist/model/loadModel.d.ts +38 -0
  124. package/dist/model/loadModel.js +122 -0
  125. package/dist/model/loadModel.js.map +1 -0
  126. package/dist/model/model.d.ts +26 -9
  127. package/dist/model/model.js +22 -17
  128. package/dist/model/model.js.map +1 -1
  129. package/dist/model/rules.d.ts +23 -0
  130. package/dist/model/rules.js +23 -0
  131. package/dist/model/rules.js.map +1 -1
  132. package/dist/model/shapeLint.js +95 -0
  133. package/dist/model/shapeLint.js.map +1 -1
  134. package/dist/model/sliceDocIndex.js +13 -5
  135. package/dist/model/sliceDocIndex.js.map +1 -1
  136. package/dist/model/sliceDocPath.d.ts +19 -0
  137. package/dist/model/sliceDocPath.js +49 -0
  138. package/dist/model/sliceDocPath.js.map +1 -0
  139. package/dist/model/storylineMap.d.ts +20 -0
  140. package/dist/model/storylineMap.js +55 -0
  141. package/dist/model/storylineMap.js.map +1 -0
  142. package/dist/model/validate.d.ts +7 -0
  143. package/dist/model/validate.js +8 -0
  144. package/dist/model/validate.js.map +1 -1
  145. package/dist/parser/ast.d.ts +19 -0
  146. package/dist/parser/parser.d.ts +17 -1
  147. package/dist/parser/parser.js +91 -2
  148. package/dist/parser/parser.js.map +1 -1
  149. package/dist/pipeline.d.ts +16 -0
  150. package/dist/pipeline.js +19 -2
  151. package/dist/pipeline.js.map +1 -1
  152. package/dist/render/drawEdges.d.ts +5 -1
  153. package/dist/render/drawEdges.js +57 -4
  154. package/dist/render/drawEdges.js.map +1 -1
  155. package/dist/render/drawNotes.d.ts +4 -1
  156. package/dist/render/drawNotes.js +40 -10
  157. package/dist/render/drawNotes.js.map +1 -1
  158. package/dist/render/render.d.ts +11 -0
  159. package/dist/render/render.js +94 -11
  160. package/dist/render/render.js.map +1 -1
  161. package/dist/render/serve.d.ts +5 -0
  162. package/dist/render/serve.js +18 -2
  163. package/dist/render/serve.js.map +1 -1
  164. package/dist/render/sliceDiagram.js +3 -0
  165. package/dist/render/sliceDiagram.js.map +1 -1
  166. package/dist/render/sliceOverlay.d.ts +4 -5
  167. package/dist/render/sliceOverlay.js +4 -7
  168. package/dist/render/sliceOverlay.js.map +1 -1
  169. package/dist/render/sliceStatus.d.ts +8 -0
  170. package/dist/render/sliceStatus.js +22 -15
  171. package/dist/render/sliceStatus.js.map +1 -1
  172. package/dist/render/storylineBands.d.ts +0 -3
  173. package/dist/render/storylineBands.js +2 -22
  174. package/dist/render/storylineBands.js.map +1 -1
  175. package/dist/render/storylineDiagram.js +17 -69
  176. package/dist/render/storylineDiagram.js.map +1 -1
  177. package/dist/render/storylineMap.d.ts +18 -0
  178. package/dist/render/storylineMap.js +263 -0
  179. package/dist/render/storylineMap.js.map +1 -0
  180. package/dist/render/svgGeometry.d.ts +9 -0
  181. package/dist/render/svgGeometry.js +16 -0
  182. package/dist/render/svgGeometry.js.map +1 -1
  183. package/dist/render/viewerHtml.js +1 -1
  184. package/dist/render/viewerHtml.js.map +1 -1
  185. package/dist/templates.d.ts +5 -1
  186. package/dist/templates.js +39 -2
  187. package/dist/templates.js.map +1 -1
  188. package/package.json +1 -1
  189. package/plugin/.claude-plugin/plugin.json +2 -2
  190. package/plugin/agents/em-implementer.md +10 -0
  191. package/plugin/agents/em-validator.md +1 -0
  192. package/plugin/shared/reference/em-dsl.md +54 -9
  193. package/plugin/shared/reference/methodology.md +35 -2
  194. package/plugin/shared/reference/operating-principles.md +26 -15
  195. package/plugin/shared/reference/slice-doc-schema.md +2 -1
  196. package/plugin/shared/templates/model-readme.md +10 -2
  197. package/plugin/shared/templates/slice.md +7 -1
  198. package/plugin/skills/conform/SKILL.md +7 -4
  199. package/plugin/skills/conform/reference/conform.md +82 -4
  200. package/plugin/skills/design/SKILL.md +23 -8
  201. package/plugin/skills/discover/SKILL.md +2 -2
  202. package/plugin/skills/discover/reference/extract.md +5 -1
  203. package/plugin/skills/engagement/SKILL.md +1 -1
  204. package/plugin/skills/engagement/reference/engagement.md +1 -0
  205. package/plugin/skills/event-modeling/SKILL.md +1 -1
  206. package/plugin/skills/implement/SKILL.md +5 -3
  207. package/plugin/skills/implement/reference/implement.md +14 -0
  208. package/plugin/skills/review/SKILL.md +19 -7
@@ -18,6 +18,16 @@ You are the em implementer. You build one slice, in one worktree, on one branch,
18
18
  - Write code and tests inside that slice's package, as the constitution's layout rule describes it.
19
19
  - Every invariant and scenario in the slice doc gets a test that cites its `INV-*` id.
20
20
 
21
+ ## Bindings file
22
+
23
+ Part of this slice's definition of done, in the same PR: write or update `conformance/bindings/<slice-key>.yaml`, beside the model, per `docs/bindings.md`'s schema — you just wrote the code, you know which file and identifier anchors each element and each `INV-*` invariant's enforcement and test sites. It's a plain file you author like any other in your PR; no `em` command writes it. Before you report the slice done, verify it:
24
+
25
+ ```
26
+ em conform bindings check <model>.em --repo <path> --slice <slice-key> --exit-code
27
+ ```
28
+
29
+ Exit 0 is required. If it fails, fix the bindings file — a wrong `ref`, a stale `file`/`identifier` pair — never report the slice done with a failing binding.
30
+
21
31
  ## Tracking key
22
32
 
23
33
  If your prompt carries a tracking key (an issue-tracker key such as `PROJ-123`, or a URL), the lead has already resolved it for this slice: the slice doc's own `tracking:` wins over the engagement's. Use it as given:
@@ -22,6 +22,7 @@ Run each one and record pass or fail:
22
22
  5. No diff under `slices/**`, `*.em`, `constitution.md`, or `engagements/`. Use `git diff --name-only <base>...HEAD`.
23
23
  6. The pull request body cites the slice doc.
24
24
  7. The pull request title equals the title you were given, exactly (`gh pr view <pr> --json title`). A mismatch fails the check and quotes both. If no title was given, skip this check and say so.
25
+ 8. `conformance/bindings/<key>.yaml` exists beside the model, and `em conform bindings check <model.em> --repo <worktree path> --slice <key> --exit-code` exits 0 (GH #450).
25
26
 
26
27
  ## Output
27
28
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling
3
- em-version: 1.20.0
3
+ em-version: 1.22.0
4
4
  description: >-
5
5
  Use when the user runs `/event-modeling` (with or without a phase name), or wants to resume an
6
6
  event-modeling session without specifying which part of the process applies. Reads the model's
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-conform
3
- em-version: 1.20.0
3
+ em-version: 1.22.0
4
4
  description: >-
5
5
  Use when checking a ratified event model (and its slice docs) for drift against the codebase
6
6
  that implements it — advisory only, never a gate, never an unprompted edit — or when walking
@@ -28,9 +28,12 @@ duplicating them.
28
28
  In short: run `em conform-scope <model-name>.em --repo <target-repo-path> --seed-asis` to
29
29
  compute the in-scope slice set (diff-scoped by default, via `Last conformance:` + the target
30
30
  repo's changed paths mapped through each slice doc's `implementedIn:`) and seed the scratch
31
- model in one step; for each in-scope slice, gather code evidence *before* comparing to the model
32
- or doc; write the as-is picture into that scratch model (`<model-name>-asis.em`, reusing the
33
- canonical model's names wherever the code matches them); run `em diff <model-name>.em
31
+ model in one step; for each in-scope slice, check for a `conformance/bindings/<key>.yaml` first
32
+ (GH #450) — verify it with `em conform bindings check` and scope the walk through it when it
33
+ exists, backfill it from this walk's own evidence when it doesn't — then gather code evidence
34
+ *before* comparing to the model or doc; write the as-is picture into that scratch model
35
+ (`<model-name>-asis.em`, reusing the canonical model's names wherever the code matches them);
36
+ run `em diff <model-name>.em
34
37
  <model-name>-asis.em --json` and let `em` decide the structural deltas; classify every finding
35
38
  (real drift / model gap / internal inconsistency / uncertainty) with cited evidence; write
36
39
  `conformance/<date>-report.md` **and its structured twin `conformance/<date>-findings.json`**
@@ -94,7 +94,7 @@ e.g. a PR link, deterministically matches nothing). It prints one JSON document
94
94
  ```json
95
95
  { "lastConformance": { "date": "...", "revision": "..." } | null,
96
96
  "changedPaths": ["..."],
97
- "candidateSlices": [ { "key": "checkout", "matchedBy": "implementedIn", "paths": ["..."] } ],
97
+ "candidateSlices": [ { "key": "checkout", "matchedBy": "implementedIn", "paths": ["..."], "file": null } ],
98
98
  "unmappedPaths": ["..."] }
99
99
  ```
100
100
 
@@ -104,6 +104,20 @@ your in-scope set for step 2. If `lastConformance` is `null` (first run) or you
104
104
  `changedPaths`/`unmappedPaths` both empty) — the tool makes this call for you, no need to
105
105
  special-case it yourself.
106
106
 
107
+ **Storyline models (GH #451).** A model may split its storylines into their own files — a root
108
+ declares `storyline "Name" [after "Other"]` with no body, and the storyline's slices live in
109
+ `<slug>/<slug>.em` (`docs/dsl.md`'s "Storyline models" section). `em conform-scope` already
110
+ compiles the root plus every storyline file it declares as ONE model
111
+ (the same way every other `em` command does — a storyline file never compiles on its own), so
112
+ scoping via `implementedIn` needs no special handling. What DOES change: each `candidateSlice`'s
113
+ `file` names the storyline file that slice's body lives in — posix, relative to the model's
114
+ directory — or `null` for a slice in the root file. This is which physical file you anchor on in
115
+ step 2 and which scratch file you write the as-is picture into in step 3 (`--seed-asis` seeds one
116
+ scratch file per physical file, not just the root — see there). Don't confuse a slice's `file`
117
+ with its doc's path: slice docs for a storyline model live under that storyline's own
118
+ `slices/` folder, but `em` finds them by key across every folder regardless, so doc resolution
119
+ itself needs no extra care.
120
+
107
121
  **`unmappedPaths` is your judgment queue, not a dead end.** A changed path the tool couldn't
108
122
  place mechanically (`implementedIn` stale, a URL-only link, code that moved) still might belong
109
123
  to an in-scope slice — a quick Grep for the path's obvious symbols against the slice docs/model
@@ -119,6 +133,31 @@ keeps a recurring conform run cheap; don't invent a smarter trigger.
119
133
 
120
134
  For each slice in scope:
121
135
 
136
+ **Check for an existing bindings file first (GH #450).** `conformance/bindings/<slice-key>.yaml`,
137
+ beside the model, records this slice's own prior evidence — file and identifier per model
138
+ element, and per `INV-*` invariant's enforcement/test sites (`docs/bindings.md`'s schema). Two
139
+ cases, mutually exclusive:
140
+
141
+ - **It exists:** verify it before walking anything by hand —
142
+ `em conform bindings check <model-name>.em --repo <target-repo-path> --slice <slice-key>`.
143
+ A binding that still checks out is evidence you don't need to re-derive; **scope the rest of
144
+ this slice's walk through it** — re-read only what the check reports failing (`file not
145
+ found`/`identifier not found`, an unknown `ref`) plus whatever `em conform-scope`'s
146
+ `changedPaths` (step 1) says moved since `implementedIn`. This is what turns a recurring
147
+ conform walk incremental instead of a full re-derivation every time — don't throw that away by
148
+ re-walking evidence the file already gives you clean.
149
+ - **It doesn't exist:** the common shape for a slice an external or ticket-driven implementer
150
+ shipped without ever running `em` (`docs/bindings.md` is written so that implementer can still
151
+ author one by hand from its own ticket spec; this backfill is the safety net for the ones that
152
+ didn't, or for any slice that shipped before GH #450). Walk the slice normally (the numbered
153
+ steps below) and, once you've gathered that evidence, **write
154
+ `conformance/bindings/<slice-key>.yaml` from it** — the same file an implementer would have
155
+ written, now backfilled from your conform evidence instead of from the implementer's own
156
+ knowledge of the code. This is advisory tooling catching up a gap, not a model or doc edit: it
157
+ needs no ratification and doesn't change this phase's advisory-only stance (see "Stance"
158
+ above) — you're recording where code already lives, not deciding what it should do. From the
159
+ next conform run on, this slice follows the "it exists" branch above.
160
+
122
161
  1. Read the slice's **doc** (`slices/<slice-name>.md`) alongside its elements in the `.em`.
123
162
  This tells you the vocabulary to anchor on and the claims to check — not the answer.
124
163
  2. Walk the code and **record evidence before comparing**:
@@ -139,6 +178,20 @@ For each slice in scope:
139
178
  as missing. This is the *general*, idiom-level version of the next bullet — prefer citing
140
179
  an actual `divergence` annotation when the element carries one; fall back to this idiom
141
180
  note only when it doesn't.
181
+ - **`updates from` sources (GH #451) — housekeeping evidence, not trigger evidence.** A view
182
+ declared `view X from "A" updates from "B", "C"` (`docs/dsl.md`'s `updates from` section)
183
+ splits its sources into what put a row on the list (`from`) and what keeps an existing row
184
+ current or removes it (`updates from`). The two need different evidence: a `from` source is
185
+ what an automation/processor reacting to this view should fire on, or what a reader expects
186
+ to see a NEW row appear for — missing evidence there is the usual structural/spec drift. A
187
+ `updates from` source is expected to have an update/delete handler for the SAME row, never a
188
+ "new item created" or "automation triggered" site — don't report the absence of trigger-type
189
+ evidence for an `updates from` source as drift; that is literally what the clause says it
190
+ isn't. `em export`'s `from` list is the union of both (so `em diff`'s structural comparison
191
+ sees every source regardless of which list it's in — folding `from`/`updates from` into each
192
+ other is NOT itself a structural change `em diff` would catch), but the *diagram* draws no
193
+ arrow for an `updates from` source and the *evidence walk* above should read the same way:
194
+ check it as "does this row get kept current/removed", not "does this cause a new row".
142
195
  - **Accepted divergences:** before treating any finding as drift, check whether the
143
196
  relevant canonical element already carries a `divergence "..."` annotation (visible in
144
197
  the `.em`, in `em export --json`'s `divergence` field, or — for a structural finding — as
@@ -161,8 +214,15 @@ For each slice in scope:
161
214
  3. Write what you found into the scratch model (`<model-name>-asis.em`, see below) — start it
162
215
  with `em conform-scope <model-name>.em --repo <target-repo-path> --seed-asis`, which
163
216
  byte-copies the canonical `.em` to `<model-name>-asis.em` and ensures `*-asis.em` is
164
- gitignored (idempotent — safe to pass on every run). **Then replace only the in-scope
165
- slices** with the as-is picture, leaving out-of-scope slices byte-identical. This is not an optimization:
217
+ gitignored (idempotent — safe to pass on every run). **Storyline models (GH #451):** when the
218
+ slice you're rewriting has a non-null `file` in step 1's `candidateSlices` (its body lives in
219
+ a storyline file, not the root), `--seed-asis` also wrote a scratch copy of THAT file — the
220
+ `seeded.storylineFiles` entry naming it — and the copied root's declaration already points at
221
+ it. Edit that storyline file's own `-asis` sibling, never `<model-name>-asis.em` (it has no
222
+ body for that slice to rewrite) and never the canonical storyline file (that corrupts the
223
+ real model, not a scratch copy). **Then replace only the in-scope slices** with the as-is
224
+ picture, leaving out-of-scope slices byte-identical, in whichever scratch file(s) hold them.
225
+ This is not an optimization:
166
226
  `em diff` matches slices by name, so a scratch model containing *only* the in-scope slices
167
227
  reports every other canonical slice as `slice-removed` plus one `element-removed` per
168
228
  element — a phantom-removal flood on every diff-scoped run, which is the exact false drift
@@ -405,7 +465,12 @@ each call adds its own stamp rather than overwriting the last.
405
465
  directory as `.event-modeling.md`). Always seeded as a copy of the canonical model with only
406
466
  the in-scope slices rewritten (step 2) — `em conform-scope --seed-asis` does the seeding and
407
467
  the gitignoring (`*-asis.em`, idempotent) mechanically, never built up from nothing and never
408
- hand-maintained. It's regenerated every conform run.
468
+ hand-maintained. It's regenerated every conform run. **Storyline models (GH #451):** a split
469
+ model gets ONE scratch file per physical file — `<model-name>-asis.em` for the root, plus a
470
+ `-asis` sibling for every storyline file the root declares (`seeded.storylineFiles` names
471
+ them) — never just the root; the copied root's `in` clause already points at each sibling, so
472
+ `em diff <model-name>.em <model-name>-asis.em` reads the whole scratch set as one model, same
473
+ as the canonical root reads the whole canonical set.
409
474
  - **Report location:** `conformance/<YYYY-MM-DD>-report.md` in the model directory. One file
410
475
  per run; don't overwrite a prior date's report. Its findings go in the same-stem `-findings.json`; gates read every same-revision findings file, so per-slice runs may write `<date>-<name>-findings.json`, and a ruling is written to the file the report names. The same-revision lookup uses the report's OWN revision (its `Target repo: … @ <rev>` header line; the caller's `<rev>` / `--as-of` only when the report has none).
411
476
  - **Red note wording:** `issue "conformance: <what's wrong, plainly>"` — plain enough that
@@ -447,6 +512,13 @@ whatever cadence the user wants (see `docs/ci.md` for a scheduled-run recipe).
447
512
  - **A scratch model holding only the in-scope slices** — `em diff` reads every omitted
448
513
  canonical slice as removed, so a diff-scoped run drowns in phantom removals. Seed from the
449
514
  canonical model and rewrite only the slices you walked.
515
+ - **Editing a storyline file's canonical copy as if it were the scratch model (GH #451)** — for
516
+ a slice whose `file` isn't the root, the scratch copy is that storyline file's OWN `-asis`
517
+ sibling (`seeded.storylineFiles`), never `<model-name>-asis.em` (no body there to rewrite) and
518
+ never the canonical storyline file itself (that's the model, not a scratch copy of it).
519
+ - **Treating an `updates from` source like a `from` source during the evidence walk** — a
520
+ missing "new row appears" or "automation fires" site is not drift for a source the view only
521
+ lists under `updates from`; check it for row-update/removal evidence instead (step 2).
450
522
  - **Reporting uncertainty as drift** — "I couldn't find the enforcement site" is an
451
523
  uncertainty, never a finding against the model.
452
524
  - **Editing the model or a slice doc without ratification** — conform proposes; the user
@@ -457,6 +529,12 @@ whatever cadence the user wants (see `docs/ci.md` for a scheduled-run recipe).
457
529
  - **Re-deriving what `em diff` already decided** — once you have the JSON, use it; don't
458
530
  eyeball the two `.em` files yourself looking for structural differences the tool already
459
531
  computed for you.
532
+ - **Re-deriving evidence a verified bindings file already gives you (GH #450)** — when
533
+ `em conform bindings check` passes for a slice's `conformance/bindings/<key>.yaml`, walking
534
+ every element and invariant by hand anyway defeats the point of the file; scope through it
535
+ (step 2) instead.
536
+ - **Skipping the backfill because a slice has no bindings file** — the absence is the signal to
537
+ write one from this walk's own evidence, not a reason to leave the gap for next time.
460
538
  - **Re-litigating an accepted divergence** — a non-null `acceptedDivergence` on a diff entry
461
539
  (or a `divergence` annotation on the element behind a spec/internal finding) means this exact
462
540
  question was already reasoned through and ratified. Classify it as Accepted divergence and
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-design
3
- em-version: 1.20.0
3
+ em-version: 1.22.0
4
4
  description: >-
5
5
  Use when structuring a draft event model into swimlanes and the four patterns (State Change,
6
6
  State View, Automation, Translation), evaluating a model's structural completeness, or writing
@@ -71,7 +71,13 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
71
71
  optional `after "<storyline>"` timeline clause: `em-dsl.md`) so the diagram gets a band per
72
72
  story and `em render --storyline "Name"` can show one alone; slices keep their file order, and
73
73
  a model can adopt storylines incrementally (`slice-outside-storyline` is only an advisory
74
- warning) — but name them before you slice, not after. Then group events into **contexts** (bounded contexts /
74
+ warning) — but name them before you slice, not after. **Once the storylines are named, give
75
+ each its own file** (GH #421): run `em storyline split <root>.em` so the root keeps the
76
+ vocabulary and the storyline list with its flow (`storyline "Name" [after "Other"]`) and each
77
+ story's slices live in `<slug>/<slug>.em` with its docs in `<slug>/slices/` — do this before any
78
+ doc is wired, so nothing moves twice. **A storyline that reads another story's events declares
79
+ `after` it** (`storyline-reads-without-after` flags the ones you miss); two stories that run in
80
+ parallel read nothing from each other. Then group events into **contexts** (bounded contexts /
75
81
  aggregates) — ask which events share a consistency boundary and who owns them. Classify each
76
82
  slice as one of the 4 patterns. **Share the slice between every automation or translation and
77
83
  the command it triggers** (the reaction — processor/translation — together with the command
@@ -91,17 +97,26 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
91
97
  read model; a child view only when its query needs what the parent view lacks), the
92
98
  **views-as-lookups check** per `from "E"` on a view (does E add, change or remove a row here? if
93
99
  it only fills a column, put the column on the event that creates the row and drop E — see
94
- `../event-modeling-shared/reference/methodology.md`, "Views as lookups"), and the
100
+ `../event-modeling-shared/reference/methodology.md`, "Views as lookups"), then the
101
+ **trigger / housekeeping split** on what is left (*which event is the reason this row appears?*
102
+ that one stays in `from`; every event that only keeps a row current or removes it moves to
103
+ `updates from` — the view still reads it, the diagram no longer draws it; a to-do list with more
104
+ than one or two inbound arrows is missing this split), the **continuation check** per `view X
105
+ again` (it names only the events that changed the view since the previous instance, never the
106
+ first instance's list again), and the
95
107
  **conditional-invariant signal** (invariants or fields that apply only "if `<field>` is set"
96
- mean two intents in one command: split it). The `timeline-entity-clustered`,
97
- `command-second-intent`, `automation-split-by-outcome` and `view-lookup-source` lints are the
98
- mechanical half (the last one only catches the cross-context cases — do the check by eye too); the
99
- 1.18.0 design exit gate is `em validate --shape`. Shape the model first, scaffold docs after: a rename,
108
+ mean two intents in one command: split it). Read each story on its own render and on the canvas
109
+ (`em render <root>.em --map`), not on the full timeline. The `timeline-entity-clustered`,
110
+ `command-second-intent`, `automation-split-by-outcome`, `view-lookup-source`,
111
+ `again-restates-sources` and `storyline-reads-without-after` lints are the
112
+ mechanical half (the lookup one only catches the cross-context cases — do the check by eye too); the
113
+ design exit gate is `em validate --shape`. Shape the model first, scaffold docs after: a rename,
100
114
  split or merge from the reshape would orphan docs already wired with `--wire`. The `slice` phase is unchanged (timeline order).
101
115
  3. **Elaborate scenarios — first pass (step 6).** Scaffold every slice's doc now, one per
102
116
  slice, none skipped: `em slice new "<slice name>" --pattern <state-change|state-view|
103
117
  automation|translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes
104
- `slices/<slice-name>.md` at `status: draft` (the same command, flags, and `--wire`
118
+ `slices/<slice-name>.md` at `status: draft` (into the slice's own storyline folder when the model
119
+ is split — pass the ROOT model to `--wire`) (the same command, flags, and `--wire`
105
120
  note-binding the `slice` phase's step 2 below documents in full). A slice whose only element
106
121
  is a `view X again` instance is a **continuation** (MIL-208), not a slice to scaffold —
107
122
  `--wire` refuses it and names the originating slice holding `X`'s first declaration; its
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-discover
3
- em-version: 1.20.0
3
+ em-version: 1.22.0
4
4
  description: >-
5
5
  Use when starting a brand-new event model from scratch — greenfield discovery: brainstorming
6
6
  past-tense events, storyboarding the happy path, finding the commands and read models — or when
@@ -68,7 +68,7 @@ Goal: a draft model of events, storyboard, commands, and views. Loose is OK; str
68
68
  **Close the loop before leaving this step:** every event from step 3 must be read by at least one
69
69
  read model. Walk the event list and ask, for each one, *"who looks at this, and what do they do
70
70
  with it?"* An event with no answer is either missing its read model or shouldn't be recorded —
71
- both are worth raising with the user rather than leaving dangling.
71
+ both are worth raising with the user rather than leaving dangling. Keep the draft in one file with inline `storyline` blocks; the design phase splits it into one file per story (`em storyline split`) once the names are settled.
72
72
 
73
73
  End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (steps done,
74
74
  decisions, open questions — run `em state set-phase model` rather than hand-editing `Current
@@ -103,7 +103,11 @@ it is fine to finish a round with `# TBD`s still parked.
103
103
  here, that would be inventing desired state. Note it as a finding in the Decisions log the same
104
104
  way as an unread event, and leave it for `model`/`slice` to apply the views-as-lookups rule
105
105
  (`../../event-modeling-shared/reference/methodology.md`, "Views as lookups") once the as-is
106
- model is agreed.
106
+ model is agreed. **Housekeeping handlers are `updates from`, as-is:** when the projection code
107
+ handles an event only to update or delete a row it already holds (a status flip, a due date
108
+ moved, an address changed, a row removed), write that event in `updates from "…"`, not `from` —
109
+ that is what the code does today, it keeps the field tracing complete, and the as-is render then
110
+ shows the one line that puts work on the list instead of every handler.
107
111
  - **R5 — Boundaries & reactions.** Current automations and integrations as-is: schedulers,
108
112
  listeners, outbound syncs, inbound webhooks. Model each as a reaction sharing its slice with
109
113
  the `command` + `event` it triggers (`reaction → command → event`, per `em-dsl.md`) — the
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-engagement
3
- em-version: 1.20.0
3
+ em-version: 1.22.0
4
4
  description: >-
5
5
  Use when leading an engagement: building a named set of ratified event-modeling slices
6
6
  (`em engagement`) into one independent PR per slice, each against main, by dispatching the
@@ -47,6 +47,7 @@ Validator (em-validator, <model>):
47
47
  | diff footprint inside the slice package | PASS |
48
48
  | no diff under `slices/**`, `*.em`, `constitution.md`, `engagements/` | PASS |
49
49
  | PR body cites the slice doc | PASS |
50
+ | `conformance/bindings/<key>.yaml` exists + `em conform bindings check --slice <key> --exit-code` | PASS |
50
51
 
51
52
  Reviewer (em-reviewer, <model>): <findings list verbatim, or NO FINDINGS>
52
53
  Critic (em-critic, <model>): <findings list verbatim, or NO FINDINGS>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-implement
3
- em-version: 1.20.0
3
+ em-version: 1.22.0
4
4
  description: >-
5
5
  Use when building a single ratified event-modeling slice (status: ready-to-implement) into
6
6
  merged, tested code — the one phase where the agent builds rather than facilitates. Gates on
@@ -28,8 +28,10 @@ ships in the originating doc's PR); ship the endpoint a
28
28
  model doesn't show; surface every gap to the user instead of deciding it silently; cover every
29
29
  `INV-<MNEMONIC>-n` and every scenario with tests, proving the citations before merge with
30
30
  `em coverage <model>.em --slice <key> --tests <dir> --strict` (the default form skips a
31
- `ready-to-implement` slice); in spec-kit projects, allocate via
32
- em-sdd-bridge (redirect mode) and never run `/speckit.specify`.
31
+ `ready-to-implement` slice); write or update the slice's `conformance/bindings/<key>.yaml`
32
+ (GH #450, `docs/bindings.md`'s schema) and verify it with `em conform bindings check <model>.em
33
+ --repo <path> --slice <key> --exit-code` before calling the slice done; in spec-kit projects,
34
+ allocate via em-sdd-bridge (redirect mode) and never run `/speckit.specify`.
33
35
 
34
36
  **Gate: no constitution, no first slice.** The constitution is the project's ratified house rules
35
37
  — stack and architectural shape (including which implementation skill each slice pattern routes
@@ -300,6 +300,20 @@ or seem small — see §10.
300
300
  you didn't touch the `.em`, so this only fails if something else broke).
301
301
  - Nothing between the slice doc and the code was committed as a source of truth (see §9 —
302
302
  work containers are ephemeral; generated or symlinked specs are renderings).
303
+ - **The slice's bindings file is written or updated** (GH #450): `conformance/bindings/<slice-
304
+ key>.yaml`, beside the model, per [`docs/bindings.md`](https://github.com/milehimikey/em/blob/main/docs/bindings.md)'s
305
+ schema. You just wrote the code — you know which file and identifier anchors each element
306
+ this slice's doc declares (`ref: <kind>.<slug>`, bare, no `<slice-key>/` prefix) and each
307
+ `INV-<MNEMONIC>-n` invariant's enforcement and test sites. This is a file contract, not an
308
+ `em`-run write: author or hand-edit the YAML directly, the same way you'd write any other file
309
+ in your PR. Verify it before calling the slice done:
310
+ ```bash
311
+ em conform bindings check <model>.em --repo <path> --slice <slice-key> --exit-code
312
+ ```
313
+ Exit 0 is required — fix the file (a wrong `ref`, a stale `file`/`identifier` pair) rather than
314
+ shipping a binding that doesn't check out. A continuation key (§5, an `again` view instance)
315
+ has no bindings file of its own; its elements and invariants are bound inside the originating
316
+ slice's file alongside the rest of that doc's bindings.
303
317
 
304
318
  ## 6. At merge: the lifecycle flip
305
319
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-review
3
- em-version: 1.20.0
3
+ em-version: 1.22.0
4
4
  description: >-
5
5
  Use when starting em's live browser viewer (`em watch --serve`) so a team can watch a model
6
6
  update in real time, or facilitating a SCHEDULED stakeholder walkthrough that steps through
@@ -15,8 +15,16 @@ description: >-
15
15
  Start the watcher with `--serve` in the background:
16
16
  `em watch <model-name>.em -o <model-name>.svg --serve` (run_in_background). It re-renders on every
17
17
  save and pushes an instant reload to the browser over Server-Sent Events. Tell the user to open the
18
- URL it prints (e.g. `http://localhost:5173/?svg=<model-name>.svg`) and share their screen — updates
19
- appear the moment you save, with no polling and no idle churn between edits.
18
+ URL it prints and share their screen — updates appear the moment you save, with no polling and no
19
+ idle churn between edits.
20
+
21
+ What that URL opens depends on the model. A model that declares storylines opens on the
22
+ **storyline canvas** (`http://localhost:5173/map`): every storyline's own diagram in a draggable
23
+ frame, the flow between storylines drawn between frames, reloaded live on every save with the
24
+ layout kept. A frame's **open ›** drills into that storyline — the viewer on its own slices and
25
+ nothing from outside it — and **‹ Storylines** in the header returns to the canvas. A model
26
+ without storylines opens on the timeline (`http://localhost:5173/?svg=<model-name>.svg`). Never
27
+ send a room to the full timeline of a storyline model; the storyline is the unit people read.
20
28
 
21
29
  The viewer navigates like a map — drag to pan, scroll/pinch to zoom, **Fit** to reset — and its
22
30
  **Review mode** steps through slices one at a time (see the `review` phase below). A save that
@@ -34,10 +42,14 @@ speculation.** Don't propose this phase proactively; it exists for facilitated r
34
42
  non-engineer stakeholders in the room, not routine model editing.
35
43
 
36
44
  Start the same live server as `watch`: `em watch <model-name>.em -o <model-name>.svg --serve`
37
- (run_in_background). Open the printed URL, click **Review mode** in the header, and share the
38
- screen. Use Prev/Next (or the left/right arrow keys) to step through slices in declaration
39
- order — each one pans/zooms into view with everything else dimmed, so the room's attention
40
- tracks one slice at a time.
45
+ (run_in_background). Open the printed URL and share the screen. With storylines, review one
46
+ storyline at a time: open it from the canvas (or start inside it with `--storyline "<name>"`),
47
+ then click **Review mode** in the header — Prev/Next walk that storyline's slices only. Between
48
+ storylines, **‹ Storylines** returns to the canvas, where the arrows between frames are the
49
+ order to take them in. Without storylines, click **Review mode** on the timeline. Use Prev/Next
50
+ (or the left/right arrow keys) to step through slices in declaration order — each one
51
+ pans/zooms into view with everything else dimmed, so the room's attention tracks one slice at
52
+ a time.
41
53
 
42
54
  **When a State View slice is on screen, ask who reads it (MIL-215):** a person or client in the
43
55
  room → the `ui` should draw it; another system → `public`; only an automation → nothing, it's