@milehimikey/em 1.16.1 → 1.18.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 (219) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +1 -1
  2. package/.claude/skills/event-modeling-conform/SKILL.md +3 -2
  3. package/.claude/skills/event-modeling-conform/reference/conform.md +7 -0
  4. package/.claude/skills/event-modeling-design/SKILL.md +46 -5
  5. package/.claude/skills/event-modeling-discover/SKILL.md +43 -8
  6. package/.claude/skills/event-modeling-discover/reference/extract.md +6 -1
  7. package/.claude/skills/event-modeling-discover/reference/prd-rubric.md +102 -0
  8. package/.claude/skills/event-modeling-discover/reference/prd-spec-path.md +171 -0
  9. package/.claude/skills/event-modeling-engagement/SKILL.md +28 -6
  10. package/.claude/skills/event-modeling-engagement/reference/engagement.md +5 -4
  11. package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
  12. package/.claude/skills/event-modeling-implement/reference/implement.md +18 -8
  13. package/.claude/skills/event-modeling-review/SKILL.md +1 -1
  14. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +107 -10
  15. package/.claude/skills/event-modeling-shared/reference/methodology.md +268 -0
  16. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +122 -15
  17. package/.claude/skills/event-modeling-shared/templates/slice.md +11 -3
  18. package/.claude-plugin/marketplace.json +2 -2
  19. package/dist/catalog/docJoin.d.ts +13 -5
  20. package/dist/catalog/docJoin.js +6 -1
  21. package/dist/catalog/docJoin.js.map +1 -1
  22. package/dist/catalog/docModelConsistencyValidate.js +160 -0
  23. package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
  24. package/dist/catalog/invariantsValidate.d.ts +2 -0
  25. package/dist/catalog/invariantsValidate.js +67 -1
  26. package/dist/catalog/invariantsValidate.js.map +1 -1
  27. package/dist/catalog/scenarioQualityValidate.d.ts +4 -0
  28. package/dist/catalog/scenarioQualityValidate.js +148 -0
  29. package/dist/catalog/scenarioQualityValidate.js.map +1 -0
  30. package/dist/catalog/scenarios.d.ts +5 -0
  31. package/dist/catalog/scenarios.js +80 -0
  32. package/dist/catalog/scenarios.js.map +1 -1
  33. package/dist/catalog/signedBodyValidate.d.ts +7 -0
  34. package/dist/catalog/signedBodyValidate.js +29 -0
  35. package/dist/catalog/signedBodyValidate.js.map +1 -0
  36. package/dist/catalog/sliceDoc.d.ts +53 -7
  37. package/dist/catalog/sliceDoc.js +89 -10
  38. package/dist/catalog/sliceDoc.js.map +1 -1
  39. package/dist/catalog/sliceReadyValidate.d.ts +23 -2
  40. package/dist/catalog/sliceReadyValidate.js +54 -22
  41. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  42. package/dist/catalog/sliceSections.d.ts +3 -2
  43. package/dist/catalog/sliceSections.js +99 -9
  44. package/dist/catalog/sliceSections.js.map +1 -1
  45. package/dist/cli/ciInit.d.ts +4 -11
  46. package/dist/cli/ciInit.js +24 -6
  47. package/dist/cli/ciInit.js.map +1 -1
  48. package/dist/cli/coverage.d.ts +9 -0
  49. package/dist/cli/coverage.js +18 -0
  50. package/dist/cli/coverage.js.map +1 -1
  51. package/dist/cli/defer.d.ts +60 -17
  52. package/dist/cli/defer.js +187 -86
  53. package/dist/cli/defer.js.map +1 -1
  54. package/dist/cli/engagement.d.ts +46 -0
  55. package/dist/cli/engagement.js +156 -1
  56. package/dist/cli/engagement.js.map +1 -1
  57. package/dist/cli/engagementFoundation.d.ts +47 -0
  58. package/dist/cli/engagementFoundation.js +164 -0
  59. package/dist/cli/engagementFoundation.js.map +1 -0
  60. package/dist/cli/frontmatterSurgery.d.ts +5 -0
  61. package/dist/cli/frontmatterSurgery.js +15 -0
  62. package/dist/cli/frontmatterSurgery.js.map +1 -1
  63. package/dist/cli/ledgerCheck.d.ts +5 -1
  64. package/dist/cli/ledgerCheck.js +50 -20
  65. package/dist/cli/ledgerCheck.js.map +1 -1
  66. package/dist/cli/markImplemented.d.ts +14 -10
  67. package/dist/cli/markImplemented.js +49 -1
  68. package/dist/cli/markImplemented.js.map +1 -1
  69. package/dist/cli/prdTriage.d.ts +109 -0
  70. package/dist/cli/prdTriage.js +378 -0
  71. package/dist/cli/prdTriage.js.map +1 -0
  72. package/dist/cli/ratify.d.ts +14 -2
  73. package/dist/cli/ratify.js +34 -6
  74. package/dist/cli/ratify.js.map +1 -1
  75. package/dist/cli/refsCheck.d.ts +78 -0
  76. package/dist/cli/refsCheck.js +153 -0
  77. package/dist/cli/refsCheck.js.map +1 -0
  78. package/dist/cli/render-inputs.d.ts +2 -0
  79. package/dist/cli/render-inputs.js +4 -0
  80. package/dist/cli/render-inputs.js.map +1 -1
  81. package/dist/cli/revise.d.ts +12 -3
  82. package/dist/cli/revise.js +43 -10
  83. package/dist/cli/revise.js.map +1 -1
  84. package/dist/cli/shape.d.ts +27 -0
  85. package/dist/cli/shape.js +77 -0
  86. package/dist/cli/shape.js.map +1 -0
  87. package/dist/cli/sliceConform.js +9 -0
  88. package/dist/cli/sliceConform.js.map +1 -1
  89. package/dist/cli/sliceIndex.d.ts +6 -1
  90. package/dist/cli/sliceIndex.js +28 -13
  91. package/dist/cli/sliceIndex.js.map +1 -1
  92. package/dist/cli/sliceNew.d.ts +4 -1
  93. package/dist/cli/sliceNew.js +26 -9
  94. package/dist/cli/sliceNew.js.map +1 -1
  95. package/dist/cli/status.d.ts +26 -5
  96. package/dist/cli/status.js +28 -6
  97. package/dist/cli/status.js.map +1 -1
  98. package/dist/cli/storylineSuggest.d.ts +26 -0
  99. package/dist/cli/storylineSuggest.js +252 -0
  100. package/dist/cli/storylineSuggest.js.map +1 -0
  101. package/dist/cli/upgrade.d.ts +15 -3
  102. package/dist/cli/upgrade.js +537 -8
  103. package/dist/cli/upgrade.js.map +1 -1
  104. package/dist/cli/upgradeReshape.d.ts +60 -0
  105. package/dist/cli/upgradeReshape.js +425 -0
  106. package/dist/cli/upgradeReshape.js.map +1 -0
  107. package/dist/cli.js +430 -57
  108. package/dist/cli.js.map +1 -1
  109. package/dist/emit/dot.d.ts +2 -0
  110. package/dist/emit/dot.js +44 -8
  111. package/dist/emit/dot.js.map +1 -1
  112. package/dist/emit/engagementJson.d.ts +4 -1
  113. package/dist/emit/engagementJson.js +27 -1
  114. package/dist/emit/engagementJson.js.map +1 -1
  115. package/dist/emit/json.d.ts +21 -1
  116. package/dist/emit/json.js +29 -2
  117. package/dist/emit/json.js.map +1 -1
  118. package/dist/emit/ledgerJson.d.ts +1 -1
  119. package/dist/emit/ledgerJson.js +6 -1
  120. package/dist/emit/ledgerJson.js.map +1 -1
  121. package/dist/emit/queryJson.d.ts +1 -1
  122. package/dist/emit/queryJson.js +3 -1
  123. package/dist/emit/queryJson.js.map +1 -1
  124. package/dist/emit/refsJson.d.ts +4 -0
  125. package/dist/emit/refsJson.js +19 -0
  126. package/dist/emit/refsJson.js.map +1 -0
  127. package/dist/emit/statusJson.d.ts +1 -1
  128. package/dist/emit/statusJson.js +6 -1
  129. package/dist/emit/statusJson.js.map +1 -1
  130. package/dist/emit/theme.d.ts +1 -0
  131. package/dist/emit/theme.js +13 -0
  132. package/dist/emit/theme.js.map +1 -1
  133. package/dist/emit/validateJson.d.ts +5 -2
  134. package/dist/emit/validateJson.js +41 -2
  135. package/dist/emit/validateJson.js.map +1 -1
  136. package/dist/layout/grid.d.ts +5 -1
  137. package/dist/layout/grid.js +93 -2
  138. package/dist/layout/grid.js.map +1 -1
  139. package/dist/mcp/server.js +79 -14
  140. package/dist/mcp/server.js.map +1 -1
  141. package/dist/model/edges.d.ts +7 -0
  142. package/dist/model/edges.js +33 -16
  143. package/dist/model/edges.js.map +1 -1
  144. package/dist/model/model.d.ts +33 -0
  145. package/dist/model/model.js +87 -3
  146. package/dist/model/model.js.map +1 -1
  147. package/dist/model/queryIndex.js +2 -2
  148. package/dist/model/queryIndex.js.map +1 -1
  149. package/dist/model/refs.js +8 -5
  150. package/dist/model/refs.js.map +1 -1
  151. package/dist/model/rules.d.ts +167 -1
  152. package/dist/model/rules.js +164 -3
  153. package/dist/model/rules.js.map +1 -1
  154. package/dist/model/shapeLint.d.ts +12 -0
  155. package/dist/model/shapeLint.js +170 -0
  156. package/dist/model/shapeLint.js.map +1 -0
  157. package/dist/model/storylines.d.ts +51 -0
  158. package/dist/model/storylines.js +188 -0
  159. package/dist/model/storylines.js.map +1 -0
  160. package/dist/model/validate.d.ts +6 -1
  161. package/dist/model/validate.js +317 -14
  162. package/dist/model/validate.js.map +1 -1
  163. package/dist/parser/againSugar.d.ts +30 -0
  164. package/dist/parser/againSugar.js +105 -0
  165. package/dist/parser/againSugar.js.map +1 -0
  166. package/dist/parser/ast.d.ts +33 -0
  167. package/dist/parser/lexer.d.ts +9 -1
  168. package/dist/parser/lexer.js +17 -2
  169. package/dist/parser/lexer.js.map +1 -1
  170. package/dist/parser/parser.js +284 -4
  171. package/dist/parser/parser.js.map +1 -1
  172. package/dist/query/format.js +1 -0
  173. package/dist/query/format.js.map +1 -1
  174. package/dist/query/verbs.d.ts +4 -0
  175. package/dist/query/verbs.js +30 -3
  176. package/dist/query/verbs.js.map +1 -1
  177. package/dist/render/drawEdges.js +100 -4
  178. package/dist/render/drawEdges.js.map +1 -1
  179. package/dist/render/render.js +20 -2
  180. package/dist/render/render.js.map +1 -1
  181. package/dist/render/sliceDiagram.js +5 -5
  182. package/dist/render/sliceDiagram.js.map +1 -1
  183. package/dist/render/storylineBands.d.ts +11 -0
  184. package/dist/render/storylineBands.js +80 -0
  185. package/dist/render/storylineBands.js.map +1 -0
  186. package/dist/render/storylineDiagram.d.ts +14 -0
  187. package/dist/render/storylineDiagram.js +142 -0
  188. package/dist/render/storylineDiagram.js.map +1 -0
  189. package/dist/roster/check.d.ts +32 -0
  190. package/dist/roster/check.js +92 -0
  191. package/dist/roster/check.js.map +1 -0
  192. package/dist/roster/context.d.ts +3 -0
  193. package/dist/roster/context.js +23 -0
  194. package/dist/roster/context.js.map +1 -0
  195. package/dist/roster/init.d.ts +15 -0
  196. package/dist/roster/init.js +52 -0
  197. package/dist/roster/init.js.map +1 -0
  198. package/dist/roster/roster.d.ts +79 -0
  199. package/dist/roster/roster.js +298 -0
  200. package/dist/roster/roster.js.map +1 -0
  201. package/package.json +1 -1
  202. package/plugin/.claude-plugin/plugin.json +2 -2
  203. package/plugin/shared/reference/em-dsl.md +107 -10
  204. package/plugin/shared/reference/methodology.md +268 -0
  205. package/plugin/shared/reference/slice-doc-schema.md +122 -15
  206. package/plugin/shared/templates/slice.md +11 -3
  207. package/plugin/skills/conform/SKILL.md +3 -2
  208. package/plugin/skills/conform/reference/conform.md +7 -0
  209. package/plugin/skills/design/SKILL.md +46 -5
  210. package/plugin/skills/discover/SKILL.md +43 -8
  211. package/plugin/skills/discover/reference/extract.md +6 -1
  212. package/plugin/skills/discover/reference/prd-rubric.md +103 -0
  213. package/plugin/skills/discover/reference/prd-spec-path.md +172 -0
  214. package/plugin/skills/engagement/SKILL.md +28 -6
  215. package/plugin/skills/engagement/reference/engagement.md +5 -4
  216. package/plugin/skills/event-modeling/SKILL.md +1 -1
  217. package/plugin/skills/implement/SKILL.md +1 -1
  218. package/plugin/skills/implement/reference/implement.md +18 -8
  219. package/plugin/skills/review/SKILL.md +1 -1
@@ -23,6 +23,7 @@ em render <file> # transpile
23
23
  em render <file> -o, --out <path> # output path (extension picks the format)
24
24
  em render <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
25
25
  em render <file> --slice <name> # render only this slice, redrawn in its own canonical pattern shape (default out: slices/<kebab-slug>.svg)
26
+ em render <file> --storyline <name> # render only this storyline: its slices in order, preceded by dimmed stub columns for what they read from outside it (and the end state of a storyline it comes `after`) (default out: storylines/<kebab-slug>.svg)
26
27
  em render <file> --emit-dot # print the generated DOT instead of rendering
27
28
  em render <file> --keep-empty-lanes # keep the API lane even when empty
28
29
  em export <file> # export a versioned JSON snapshot of the normalized model
@@ -69,29 +70,34 @@ em slice index <file> # rewrite th
69
70
  em slice index <file> --check # verify the table is current; exit non-zero on drift without writing (CI)
70
71
  em slice mark-implemented <file> <slice-key> <pr-url> # flip a slice doc's frontmatter to `status: implemented` / `implementedIn: <pr-url>` — the one edit an implementing agent makes to a ratified doc at merge (MIL-103, replaces the em-sdd-bridge `em-sdd-mark-implemented` script; see reference/implement.md §6). Applies only to a `ready-to-implement` doc (MIL-277: a `draft`/`reviewed` doc refuses — ratify first). Also records the shipped version beside the link (MIL-284): `shippedVersion:`, `shippedRef:` (the doc's `ratifiedRef:`, when it has one) and `shippedOn:`, and opens the next version as a `draft` when an Open Question was deferred to it (MIL-283). Idempotent on the same URL — also after that draft opened; refuses to overwrite a different one; never touches the doc body except to re-open those deferred questions
71
72
  em slice mark-implemented <file> <slice-key> <pr-url> --on <date> # merge date recorded as `shippedOn:`, YYYY-MM-DD (default: today, local date; MIL-284)
73
+ em slice mark-implemented <file> <slice-key> <pr-url> --json # print a JSON document instead of text: { path, changed, implementedIn, nextDraft: { version, reopened[] } | null } (GH #285)
72
74
  em slice review <file> <slice-key> # flip a slice doc's frontmatter to `status: reviewed` and record `reviewedBy:`/`reviewedOn:` — the FIRST of the two human gates (MIL-201, docs/process.md#the-slice-lifecycle-gates): the room walked this slice and every open question it raised is resolved. Ratification is a separate, later gate (`em slice ratify`). Legal from `status: draft`; idempotent on the same --by/--on pair; refuses to overwrite a different one already recorded; refuses a doc already `ready-to-implement`/`implemented`; never touches `version:` or the doc body
73
75
  em slice review <file> <slice-key> --by <name> # the reviewer's (or facilitator's) name
74
76
  em slice review <file> <slice-key> --on <date> # review date, YYYY-MM-DD (default: today, local date)
75
- em slice ratify <file> <slice-key> # flip a slice doc's frontmatter to `status: ready-to-implement` and record `ratifiedBy:`/`ratifiedOn:` — the handoff sign-off (MIL-165, docs/process.md#the-slice-lifecycle-gates) that makes who ratified, and when, a first-class recorded fact. The review gate is a check on the doc's current `status:`, not on whether `reviewedBy:`/`reviewedOn:` are populated: a doc at `reviewed` passes; a `ready-to-implement` doc only when the same signer is completing their own record (since 1.15.0, MIL-283 — a new sign-off is a new version: `em slice revise`); any other status — a `draft` that never went through `em slice review` — is refused unless --skip-review is passed. Idempotent on the same --by/--on pair; refuses to overwrite a different one already recorded, and (MIL-284) a same-pair re-run on a doc whose body moved after sign-off; never touches `version:` or the doc body
77
+ em slice review <file> <slice-key> --roster <path> # ratifier roster to check --by against (GH #259; default: the nearest ratifiers.yaml above the model, none = unrestricted)
78
+ em slice ratify <file> <slice-key> # flip a slice doc's frontmatter to `status: ready-to-implement` and record `ratifiedBy:`/`ratifiedOn:` — the handoff sign-off (MIL-165, docs/process.md#the-slice-lifecycle-gates) that makes who ratified, and when, a first-class recorded fact. The review gate is a check on the doc's current `status:`, not on whether `reviewedBy:`/`reviewedOn:` are populated: a doc at `reviewed` passes; a `ready-to-implement` doc only when the same signer is completing their own record (since 1.15.0, MIL-283 — a new sign-off is a new version: `em slice revise`); any other status — a `draft` that never went through `em slice review` — is refused unless --skip-review is passed. Idempotent on the same --by/--on pair; refuses to overwrite a different one already recorded, and (MIL-284) a same-pair re-run on a doc whose body moved after sign-off; never touches `version:` or the doc body. Inside a git work tree also records `ratifiedRef:` (HEAD, refusing a doc with uncommitted changes) and always `ratifiedHash:`; with --no-ref (GH #255) only the hash — for a repo whose squash/rebase merges would leave the ref unreachable — and git is not consulted
76
79
  em slice ratify <file> <slice-key> --by <name> # the ratifier's name
80
+ em slice ratify <file> <slice-key> --no-ref # record ratifiedHash: only, no ratifiedRef: (GH #255) — the signed content keeps its identity, nothing points at a commit a squash or rebase merge would orphan; the uncommitted-changes check does not apply
77
81
  em slice ratify <file> <slice-key> --on <date> # ratification date, YYYY-MM-DD (default: today, local date)
78
- em slice ratify <file> <slice-key> --skip-review # ratify without a recorded review — prints a loud notice on stderr
82
+ em slice ratify <file> <slice-key> --roster <path> # ratifier roster to check --by against (GH #259; default: the nearest ratifiers.yaml above the model, none = unrestricted)
83
+ em slice ratify <file> <slice-key> --skip-review # ratify without a recorded review — prints a loud notice on stderr and records `reviewSkipped: <status>` on the doc (GH #260) so the bypass is visible to later readers, `em export` and `--slice-ready --json`; cleared by `em slice revise`
79
84
  em slice ratify <file> <slice-key> --meaning-unchanged # API-first sign-off (MIL-238): this version does not change what the public contract means — records `meaningConfirmed: true`; required (or --contract-change) on a slice touching a `public` element
80
85
  em slice ratify <file> <slice-key> --contract-change <why> # API-first sign-off (MIL-238): a consumer must read this change differently, and why — records `contractChange: "<why>"`
81
- em slice revise <file> <slice-key> # open the next version of a slice doc as a `draft` (MIL-283): bumps `version:`, flips `status:` to `draft`, clears the prior version's review and sign-off (`reviewedBy/On`, `ratifiedBy/On/Ref/Hash`, `meaningConfirmed`/`contractChange`) and re-opens the questions deferred to the new version. The shipped version stays shipped: `implementedIn:` and the `shipped*` keys are untouched (written first on a doc shipped before 1.15). Applies to `status: implemented` (the shipped case) and to a ratified `ready-to-implement` doc (ratification withdrawn, never shipped); a `ready-to-implement` doc with no `ratifiedBy:` reopens as draft at the same version; `draft`/`reviewed` refuse — the next version is already open. The new version then takes the normal road: `em slice review --by`, `em slice ratify --by`. Questions queued by `em slice defer` for the new version (GH #236) are injected into its Open Questions. A ratified, never-shipped doc REFUSES without --withdraw (revising withdraws the only implementable version; queue a question with `em slice defer` instead)
86
+ em slice revise <file> <slice-key> # open the next version of a slice doc as a `draft` (MIL-283): bumps `version:`, flips `status:` to `draft`, clears the prior version's review and sign-off (`reviewedBy/On`, `ratifiedBy/On/Ref/Hash`, `meaningConfirmed`/`contractChange`) and re-opens the questions deferred to the new version. The shipped version stays shipped: `implementedIn:` and the `shipped*` keys are untouched (written first on a doc shipped before 1.15). Applies to `status: implemented` (the shipped case) and to a ratified `ready-to-implement` doc (ratification withdrawn, never shipped); a `ready-to-implement` doc with no `ratifiedBy:` reopens as draft at the same version; `draft`/`reviewed` refuse — the next version is already open. The new version then takes the normal road: `em slice review --by`, `em slice ratify --by`. A ratified, never-shipped doc REFUSES without --withdraw (revising withdraws the only implementable version; record a question with `em slice defer` instead — it sits outside the hash, GH #253). With --keep-version (GH #262) a ratified, never-shipped doc reopens as a draft at the SAME `version:` — same status flip and sign-off clearing, no bump — for a project that holds every slice at its initial version until a first release; refused on a shipped doc
82
87
  em slice revise <file> <slice-key> --withdraw # GH #236: required on a ratified, never-shipped doc — confirms withdrawing the ratification of the only implementable version
88
+ em slice revise <file> <slice-key> --keep-version # GH #262: reopen a ratified, never-shipped doc as a draft at the same `version:` (no bump) — a correction before a first release, not a delta; refused on a shipped doc, whose version must stay distinct from the next one
83
89
  em slice reratify <file> <slice-key> # DEPRECATED alias of `em slice revise` (MIL-283, since 1.15.0; removed in a later minor). Until 1.14 this left the doc at an unsigned `ready-to-implement`; it now opens a `draft` of the next version like `revise`. The --meaning-unchanged/--contract-change flags are accepted and ignored with a notice — the API-first confirmation is taken by `em slice ratify` on the new version
84
90
  em slice reratify <file> <slice-key> --meaning-unchanged # ignored since 1.15.0 (see above)
85
91
  em slice reratify <file> <slice-key> --contract-change <why> # ignored since 1.15.0 (see above)
86
92
  em slice reratify <file> <slice-key> --withdraw # see `em slice revise --withdraw`
87
- em slice defer <file> <slice-key> [question] # record a question this version will not answer as deferred to a later version (MIL-275): rewrites the single unchecked `## Open Questions` item containing <question> as `- [x] <item> — v<current>: <decision>; deferred to v<n> (<date>[, <by>])` so `--slice-ready` passes without a guess, and mirrors it into `.event-modeling.md` (the parking lot and a dated Decisions-log bullet; the file must exist, it is never created). Idempotent; `em slice reratify` re-opens the item when it bumps to v<n>. With --from-issue <text> instead of <question> (MIL-281): promotes the single open `issue "..."` on the slice's own elements containing <text> into the doc's Open Questions as that same deferred item, mirrors it, and removes the `issue` clause from the .em — the one-command path for a question raised in a review session. On a RATIFIED (`ready-to-implement` + `ratifiedBy:`) or shipped (`implemented`) doc (GH #236) the doc is NOT edited — its body is the signed spec — and the question is queued in `.event-modeling.md` for v<N+1>; `em slice revise` / `mark-implemented` inject it into the next draft
93
+ em slice defer <file> <slice-key> [question] # record a question this version will not answer as deferred to a later version (MIL-275): rewrites the single unchecked `## Open Questions` item containing <question> as `- [x] <item> — v<current>: <decision>; deferred to v<n> (<date>[, <by>])` so `--slice-ready` passes without a guess, and mirrors it into `.event-modeling.md` (the parking lot and a dated Decisions-log bullet; the file must exist, it is never created). Idempotent; `em slice reratify` re-opens the item when it bumps to v<n>. With --from-issue <text> instead of <question> (MIL-281): promotes the single open `issue "..."` on the slice's own elements containing <text> into the doc's Open Questions as that same deferred item, mirrors it, and removes the `issue` clause from the .em — the one-command path for a question raised in a review session. On a RATIFIED (`ready-to-implement` + `ratifiedBy:`) or shipped (`implemented`) doc (GH #253) the already-deferred `[x]` item is APPENDED to the doc's Open Questions — `ratifiedHash` excludes the section heading and its `deferred to v<n>` items, so the signature, `--slice-ready` and ratify's re-run stay valid, and the question is visible in the ratified doc; a pre-1.17 whole-body hash is re-stamped in the same edit. Refuses a doc whose body already fails its hash
88
94
  em slice defer <file> <slice-key> [question] --until <version> # the version that must answer it, v<n> (greater than the doc's version)
89
95
  em slice defer <file> <slice-key> [question] --decision <text> # what this version does instead
90
96
  em slice defer <file> <slice-key> [question] --from-issue <text> # defer a captured `issue "..."` on this slice instead of a doc Open Question (case-sensitive substring of its text)
91
97
  em slice defer <file> <slice-key> [question] --by <name> # who deferred it
92
98
  em slice defer <file> <slice-key> [question] --on <date> # deferral date, YYYY-MM-DD (default: today, local date)
93
99
  em slice conform <file> <slice-key> # record per-slice-per-version conformance certification: sets `conformedVersion:`/`conformedAt:`/`conformedOn:` on a slice doc (MIL-214) — the fact `driftSignal: in-sync` now depends on. Legal only for a shipped slice (`status: implemented`, or any status with a `shippedVersion:` — MIL-283) with an `implementedIn:` link; `conformedVersion:` is the shipped version. Idempotent on the same (version, --at) pair; a different --at simply overwrites (a later re-certification is normal — there's no way for em to tell 'later' from 'earlier' for an arbitrary revision string, so there's no --force to reach for). Refuses when the newest conformance/*-findings.json matching --at still has an unruled finding in scope for this slice; --skip-findings-check overrides with a loud notice.
94
- em slice conform <file> <slice-key> --at <rev> # the target-repo revision this certification sweep diffed against
100
+ em slice conform <file> <slice-key> --at <rev> # the target-repo revision this certification sweep diffed against — or, when what was exercised is not a commit that stays reachable (a per-PR environment under squash/rebase merges, a build or image), an opaque `<kind>:<id>` such as build:1234 or deploy:eu-1-2026-10-15 (GH #261); recorded verbatim, skipped by git-reachability checks
95
101
  em slice conform <file> <slice-key> --on <date> # certification date, YYYY-MM-DD (default: today, local date)
96
102
  em slice conform <file> <slice-key> --skip-findings-check # certify even with unruled findings in scope — prints a loud notice on stderr
97
103
  em changelog <file> # render a model's git history as a business-readable ledger (see docs/cli.md)
@@ -127,6 +133,10 @@ em conform-supersede <file> <report-path> --locus <locus> # who/what t
127
133
  em conform-supersede <file> <report-path> --by <name> # who ruled on the named finding(s) (requires --locus)
128
134
  em conform-findings check <path> # shape-validate a conformance/<date>-findings.json file (findingsSchemaVersion, required fields, enum values, ids sorted+unique, a ruled finding carries resolvedBy+resolvedOn) — exit 1 on shape errors. The conform skill writes this file directly alongside its report; this is how a headless run verifies what it just wrote.
129
135
  em conform-findings check <path> --json # print a JSON document instead of the text report
136
+ em discover triage-check <file> # validate a discovery/prd-triage.json (prdTriageSchemaVersion 1.0): shape, the shipped rubric version, every statement/storyline quote appearing verbatim in the input document, and every statement carrying a category, an outcome and (modeled/rewritten) a group + storyline — exit 1 on errors. The discover skill writes this file directly; this is how a headless run verifies it.
137
+ em discover triage-check <file> --source <path> # the input PRD/spec to check quotes against (default: the file's own `source`, relative to it)
138
+ em discover triage-check <file> --markdown # after a passing check, print the Markdown view of the record instead of the summary
139
+ em discover triage-check <file> --json # print a JSON document instead of the text report
130
140
  em watch <file> # re-render on every save
131
141
  em watch <file> -o, --out <path> # output path (extension picks the format)
132
142
  em watch <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
@@ -139,9 +149,20 @@ em validate <file> --list-divergences # print only
139
149
  em validate <file> --list-public # print only commands, events and views marked `public` (slice, kind, name, line) — an integration-surface audit, never fails the build
140
150
  em validate <file> --fail-on-issues # exit non-zero if the model has any open `issue`s (opt-in — issues are warnings and don't block by default)
141
151
  em validate <file> --slice-ready <key> # readiness gate for one slice (export key): status ready-to-implement, doc resolvable via note binding, zero unchecked Open Questions, a recorded ratifiedBy, and — for a slice touching a `public` element — a current contracts/<model key>.tsp; exits non-zero if not ready (MIL-87, MIL-259, MIL-238)
152
+ em validate <file> --require-ratified-hash # with --slice-ready (GH #256): strict mode — a ready-to-implement doc with no `ratifiedHash:` is NOT ready (`slice-ready-missing-ratified-hash`, error; `ready: false` in --json). Off by default so docs ratified before em 1.15 keep passing; turn it on once legacy docs are re-ratified
153
+ em validate <file> --strict # promote advisory lints (GH #293: scenario-quality warnings) to errors: they fail the run and, with --slice-ready, block readiness
154
+ em validate <file> --roster <path> # with --slice-ready (GH #259): the ratifier roster to check `ratifiedBy` against (default: the nearest ratifiers.yaml above the model; none = unrestricted). A name the roster does not permit for the slice's context is `ratified-by-not-in-roster` (error)
155
+ em validate <file> --check-signed-bodies # opt-in (GH #257): compare every slice doc that records a `ratifiedHash:` — ready, implemented, any status — to its body; a mismatch is `slice-body-changed-since-ratification` (error). A pre-1.17 whole-body hash with an intact body passes; a deferral written by `em slice defer` is outside the hash. The one check that still watches a shipped doc's signed content
156
+ em validate <file> --shape # GH #282: run only the advisory shape-family lints (storyline shape) and print rule / count / first ref / docs anchor; exit 0 always. With --json: `shapeSchemaVersion` 1.0 with per-rule finding arrays
157
+ em validate <file> --waive-file <path> # with --shape: a baseline file (`<rule-code> <slice-key>` per line, or a bare slice key for every shape rule; `#` comments) whose shape findings are listed as waived and not counted; missing file = no waivers
142
158
  em validate <file> --json # print a JSON document instead of text — works on a model WITH errors, unlike `em export` (MIL-128, see docs/cli.md); exit codes are unchanged
143
159
  em migrate <file> # rewrite the old two-slice Automation/Translation shape into the merged single-slice shape MIL-120 made canonical (see docs/cli.md)
144
160
  em migrate <file> --write # apply the rewrite to the file (default: dry run — report only, write nothing)
161
+ em refs <file> # check that every commit a slice doc records — `ratifiedRef:`, `shippedRef:`, a bare `conformedAt:` — still resolves and is reachable from a branch (GH #258): per doc, per field, `ancestor` / `not-ancestor` / `unresolved` / `undecidable` (a shallow clone never passes). Squash and rebase merges orphan the PR-branch HEAD ratify recorded; nothing else in em resolves a ref. Opt-in CI check, like `em ledger` — never part of `em validate`. Local object store only, no network. Kinded `conformedAt` values (build:…) are skipped; docs with no ref fields are not reported
162
+ em refs <file> --reachable-from <rev> # the branch or commit every ref must be an ancestor of (e.g. origin/main)
163
+ em refs <file> --waive <slice-key> # excuse this slice's non-ancestor/unresolved/undecidable results from the exit code (repeatable); still reported
164
+ em refs <file> --waive-file <path> # a baseline file of slice keys to waive, one per line (`#` comments allowed); missing file = no waivers
165
+ em refs <file> --json # print a JSON document instead of the text report (see docs/cli.md)
145
166
  em ledger <file> # check slice docs' version: field agrees with their content across two git revisions (opt-in CI check, MIL-89 — never part of `em validate`, see docs/ci.md)
146
167
  em ledger <file> --from <rev> # baseline revision
147
168
  em ledger <file> --to <rev> # compare revision (default: current working tree)
@@ -187,6 +208,7 @@ em query slices <files> --status <s> # the slice'
187
208
  em query slices <files> --context <c> # match a slice with an event in this @Context
188
209
  em query slices <files> --persona <p> # match a slice with a ui in this @Persona
189
210
  em query slices <files> --tag <t> # match a slice with an event carrying this tag key
211
+ em query slices <files> --storyline <name> # match a slice declared in this `storyline` block
190
212
  em query slices <files> --json # print a JSON document instead of the text report
191
213
  em query invariant <files> # declaring slice + doc facts for one INV-* id, and (with --tests) its test citations
192
214
  em query invariant <files> --id <inv-id> # the INV-* id to look up
@@ -210,6 +232,9 @@ em engagement new <file> <slug> --tracking <key> # an issue-t
210
232
  em engagement new <file> <slug> --force # overwrite an existing engagement file with this slug
211
233
  em engagement plan <file> <slug> # the engagement's build plan: dependency levels over model.edges (loops-to excluded), and per slice its readiness, branch impl/<key>, base (always main — MIL-280), and any hold
212
234
  em engagement plan <file> <slug> --json # print a JSON document instead of text (same document as the MCP engagement_plan tool)
235
+ em engagement foundation <file> <slug> # GH #240: is the foundation PR (implement.md §8) actually in the code for the events this engagement's slices emit and consume? A text scan of --code: an event is present when a file carries an identifier derived from its name (OrderPlaced / order_placed / ORDER_PLACED / order-placed), its model fields and tag fields are then looked for in those files. Per event: present | incomplete (missing-field / missing-tag, named) | missing-event — so "foundation absent" and "foundation incomplete" are different reports. Exits 1 unless every event is present. Proves the contract was written down, not that it type-checks
236
+ em engagement foundation <file> <slug> --code <dir> # the target repository (or the directory the constitution's "where a slice's code lives" rule points at)
237
+ em engagement foundation <file> <slug> --json # print a JSON document instead of text (same document as the MCP engagement_foundation tool)
213
238
  em engagement set <file> <slug> <key> # the only write path to the Ledger: set one slice's state (and optionally branch/base/pr); idempotent; merged is terminal; --state held records a human hold
214
239
  em engagement set <file> <slug> <key> --state <state> # one of: planned | building | validating | review | awaiting-merge | merged | held | gap
215
240
  em engagement set <file> <slug> <key> --branch <name> # the slice's branch
@@ -218,6 +243,12 @@ em engagement set <file> <slug> <key> --pr <url> # the slice'
218
243
  em engagement status <file> <slug> # the Ledger joined with each slice doc's current status, plus whether the engagement is closable
219
244
  em engagement status <file> <slug> --json # print a JSON document instead of text (same document as the MCP engagement_status tool)
220
245
  em engagement close <file> <slug> # set status: closed — refuses unless every slice is merged (recorded or inferred) or gap
246
+ em roster init <file> # write ratifiers.yaml beside the model, seeded from the names already recorded in the slice docs' `ratifiedBy:`/`reviewedBy:` (whole-model entries; narrow them with `contexts:` by hand). Refuses to overwrite an existing file unless --force; `em upgrade`'s grandfathered placeholder is not a name
247
+ em roster init <file> --roster <path> # write the roster here instead of <model dir>/ratifiers.yaml
248
+ em roster init <file> --force # overwrite an existing roster file
249
+ em roster check <file> # check EVERY slice doc's recorded signers against the ratifier roster (GH #259) - `ratifiedBy` on ready-to-implement and implemented docs, `reviewedBy` on any doc - the whole-model CI gate that `--slice-ready <key>` (one ready slice) is not. Exit 1 on any violation or an invalid roster; no roster file = nothing to check, exit 0. The `grandfathered ...` placeholder is exempt (counted)
250
+ em roster check <file> --roster <path> # roster to check against (default: the nearest ratifiers.yaml above the model)
251
+ em roster check <file> --json # print a JSON document (per-doc results, diagnostics with refs); exit codes unchanged
221
252
  em system [target] # verify a system — every model's `consumes` refs resolved against the other models' `public` events/views, the cross-model half of "both ends of a flow" (MIL-194/MIL-235, see docs/cli.md). Reads system.yaml when given (or found in the directory), else discovers every *.em in the repo
222
253
  em system [target] --json # print a JSON document instead of the text report (see docs/cli.md)
223
254
  em system codeowners [target] # generate (or --check) the managed CODEOWNERS block that routes review: each model's team on its design directory, and every team that `consumes` a model's public surface on that model's contract file (MIL-234, see docs/ci.md and docs/cli.md)
@@ -242,10 +273,13 @@ em ci init <model> # install .g
242
273
  em ci init <model> --tests <dir> # test directory the coverage/status steps scan for INV-* citations
243
274
  em ci init <model> -f, --force # replace an existing workflow file that has no GENERATED markers, or a managed block generated for a different set of models
244
275
  em ci init <model> --check # verify both files match the current preset; exit non-zero on drift without writing (CI)
276
+ em storyline suggest <file> # advisory (GH #290): propose `storyline` blocks for a model that has none — one per distinct slice `source "url"`, the remaining slices grouped by the view-instance chains (`again`) they feed — printed as a unified diff on stdout (apply with `patch -p1` or `git apply`); never writes the model
245
277
  em upgrade <file> # bring a model repo authored under an older em (1.6 forward) up to the installed version: an ordered list of mechanical steps (skill bundle, reaction shape, state-file bullets, generated CI blocks, constitution scaffold) plus a human list of things no command can safely decide by itself (MIL-219, see docs/upgrading.md). Dry-run by default; --apply makes one git commit per applicable step, refusing on a dirty working tree
246
278
  em upgrade <file> --apply # apply every applicable mechanical step, one git commit each, then a final `Em version:` commit
247
279
  em upgrade <file> --check # exit non-zero only on a hard incompatibility (an existing but unparseable state file, an un-migratable .em shape) — writes nothing; what CI runs; the final line says why
248
280
  em upgrade <file> --json # print a JSON document instead of text (dry-run/--check only, never with --apply)
281
+ em upgrade <file> --fold-continuations # also run the opt-in fold-continuations step (stack consecutive continuations of one view into the first; continuationOf, doc join and index rows unchanged)
282
+ em upgrade <file> --to-sugar # (implies --fold-continuations; without --apply it is a dry run listing exactly what would fold): fold continuations into their host command slice as `view X again … shown on` lines — CHANGES continuationOf and the doc join to `<host>--again` and removes the folded slices' index rows
249
283
  ```
250
284
  <!-- GENERATED:cli:end -->
251
285
 
@@ -262,6 +296,10 @@ model "Name" [owner "Team"] # diagram title; optional owning team(s), quote
262
296
  persona Name # a UI swimlane row (actor)
263
297
  context Name # an event swimlane row (bounded context / aggregate)
264
298
 
299
+ storyline "Name" [source "url"] [after "Other"] { # optional chapter grouping slices (see Storylines)
300
+ slice "Name" { ... }
301
+ }
302
+
265
303
  slice "Name" [source "url"] { # one vertical time step (a column); source is optional
266
304
  ui Free Text @Persona # screen; @Persona picks its row (defaults to first/"User")
267
305
  command Free Text # state-changing request (API band)
@@ -284,7 +322,7 @@ type Name { field: Type, ... } # named structured type, reusable from any f
284
322
  | `ui` | persona | screen / interface | `@Persona` | `note`, `issue`, `divergence`, `{ fields }` |
285
323
  | `command` | API | state-changing request | — | `note`, `issue`, `divergence`, `renamed from`, `{ fields }`, standalone `invariant` line |
286
324
  | `view` | API | read model / projection | — | `from "Event"…`, `note`, `issue`, `divergence`, `public`, `{ fields }` |
287
- | `event` | context | recorded fact (past tense) | `@Context` | `note`, `issue`, `divergence`, `public`, `tag`, `renamed from`, `loops-to`, field-level `assigned`, `{ fields }`, standalone `invariant` line |
325
+ | `event` | context | recorded fact (past tense); `event A \| B @Context` = alternative outcomes of ONE command, one slice, one shared cell | `@Context` | `note`, `issue`, `divergence`, `public`, `tag`, `renamed from`, `loops-to`, field-level `assigned`, `{ fields }`, standalone `invariant` line |
288
326
  | `processor` / `automation` / `saga` / `translation` | automation | system reaction / adapter | — | `from "…"`, `note`, `issue`, `divergence`, `{ fields }`; `translation` only: `consumes` |
289
327
 
290
328
  ### Clauses
@@ -300,6 +338,15 @@ type Name { field: Type, ... } # named structured type, reusable from any f
300
338
  "to-do list") and pointing the reaction at that view. Name that view after the pending work
301
339
  (`Payments To Process`), never after the triggering event — a view reusing the event's name
302
340
  collides in the shared namespace and draws a duplicate-name warning.
341
+ - **One event recorded by several commands** (two intents, one fact — `Create Product` and
342
+ `Create Product Variant` both ending in `Product Created`): repeat the event BARE in the later
343
+ slice with the same name and context. A bare repeat of an event with the same name and context is
344
+ ONE logical event — no `duplicate-name`, no marker (`again` stays view-only). The repeat inherits
345
+ the first declaration's fields, element-level `tag` clauses and `public`; declaring different ones
346
+ is the error `shared-event-field-mismatch`. A view's `from "E"` draws an arrow from every recording
347
+ site at or before it, and EVERY site needs a reader at or after it (add `view V again from "E"`
348
+ after the last site, or `event-unread` warns for that site). The same name in a different context
349
+ is still a `duplicate-name` warning.
303
350
  - **`again`** (views only): `view <Name> again [from "Event", …]` declares a later instance of an
304
351
  already-declared read model — the forward-only device for a view that evolves as later events
305
352
  land. Instances are ONE logical view: the first declaration owns the `note` binding; each
@@ -431,7 +478,11 @@ type Name { field: Type, ... } # named structured type, reusable from any f
431
478
  into doc-only prose** — the marker keeps `em export`/the model aware the field exists (the
432
479
  `doc-model-field-mismatch` doc↔model check does not compare view field tables at all today —
433
480
  see the slice template's field-table guidance — so this is an export-side win specifically,
434
- not yet a doc-drift check).
481
+ not yet a doc-drift check). **Collection fields (GH #288):** a view field typed `T[]` for a
482
+ declared `type T`, with `derived from "Child Created", "Child Deleted"`, embeds a child entity:
483
+ each field of `T` must trace to a named event (`fields-completeness/view-field-no-source`
484
+ otherwise). No add/remove/key clauses — `em slice sync` writes a `Collection of` note plus
485
+ one Given/Then scenario stub per named event in the view region (copy into `## Scenarios`), and `em export` sets `collection: true`.
435
486
  - **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
436
487
  one-per-line. Types are free text (no semantic checking) UNLESS the type string names a
437
488
  declared `type` (see Named types below), in which case it resolves to a structured
@@ -460,6 +511,22 @@ type Name { field: Type, ... } # named structured type, reusable from any f
460
511
  of relying on prose. Purely metadata — no visual marker, not validated. Don't confuse it with
461
512
  an element's `note "path.md"` (a markdown file link, not a URL).
462
513
 
514
+ - **`storyline "Name" [source "url"] [after "Other"] { slice … }`** (GH #277) groups slices into a
515
+ named chapter — the main journey, and secondary stories (retire a product, extend an attribute).
516
+ Slices keep their file-order timeline position; a slice outside any storyline is allowed;
517
+ storylines don't nest and a repeated name continues that storyline (its slices join it; a continuation may not add or change
518
+ `source`/`after` — parse error). The full render draws a
519
+ labelled band above the slice headers; `em render --storyline "Name"` draws one story alone, with the
520
+ elements it reads from outside as dimmed stub columns. `em export` carries `slice.storyline` and
521
+ `model.storylines[]`; `em query slices --storyline`; `em slice index` adds a Storyline column. Once
522
+ any storyline exists a slice outside every one is the advisory warning `slice-outside-storyline`.
523
+ `after "<other>"` reads the storyline as if its slices followed the referenced storyline's last
524
+ slice: `from`/`again`/reaction `from` resolve against the composed timeline (referenced → this), the
525
+ forward-only checks use it, a backward feed into the referenced storyline, an `after` cycle
526
+ (`storyline-after-cycle`), an unknown target (`storyline-after-unknown`) or a storyline declared above
527
+ the one it comes after when a flow would draw right-to-left (`storyline-after-out-of-order`) is an
528
+ error. Without `after`, file order is the only timeline.
529
+
463
530
  ### Named types
464
531
 
465
532
  `type Name { field: Type, ... }` declares a reusable structured shape at the top level (any
@@ -622,8 +689,9 @@ slice "Read Quote — created" {
622
689
  **prefer `view X again`** for every instance after the first (see Clauses) — **each instance
623
690
  carries its own consumer** (the `ui` that reads it, or a reaction), not just the last one. `again`
624
691
  instances are exempt from the duplicate-name warning even when referenced, and each reference
625
- resolves to the right instance — a plain repeat only stays warning-free while nothing references it by name, and
626
- resolves to the *first* declaration when something does. **Wire each event to a read model exactly
692
+ resolves to the right instance — a plain repeat of a view only stays warning-free while nothing references it by name, and
693
+ resolves to the *first* declaration when something does (the exception is a same-context event
694
+ repeat, which is one logical event — see above). **Wire each event to a read model exactly
627
695
  once:** a repeated instance's `from` lists only the **new** events since the previous instance
628
696
  (not cumulative), or the event draws a duplicate arrow to the same read model at every repeat. (An
629
697
  event may still feed several *different* read models, once each.)
@@ -644,6 +712,13 @@ consumer is an obligation to ship an endpoint, no `ui` means internal-only, and
644
712
  the published integration surface — see `reference/implement.md`'s "Interface obligations"
645
713
  paragraph in the `event-modeling-implement` skill (§2).
646
714
 
715
+ - **Continuations without ceremony (GH #287).** Several `view X again` lines may share one
716
+ continuation slice (they stack in the `api` cell); `view X again from "E"` written inside a
717
+ command's own slice is sugar for a synthesized continuation column right after it (one column per host slice, lines stacked; key
718
+ `<slice-key>--again`, no doc, not in `em slice index`; consumer = the slice's first `ui`,
719
+ or `shown on "Screen"` — required in a reaction slice with no `ui`); and a continuation slice may omit its name — it is auto-named
720
+ `"<View> View: <Event>"`. Instances still never connect.
721
+
647
722
  ### Reaction wiring — no positional gotcha
648
723
 
649
724
  A reaction's command is inferred from **same-slice presence**, exactly like `ui -> command`: put
@@ -664,7 +739,13 @@ it to the slice that displays the read model, or drop it.
664
739
 
665
740
  **Errors (must fix):**
666
741
  1. **Band collision** — two elements of the same band in one slice (e.g. two `command`s, or two
667
- `ui`s in the *same* persona row). Split them into separate slices/personas.
742
+ `ui`s in the *same* persona row). Split them into separate slices/personas. Two events on one
743
+ context that are alternative outcomes of one command are NOT a split: write
744
+ `event A | B @Context` (identical payloads) or `oneof { event A @Context { … } event B @Context { … } }`
745
+ (own fields/clauses per alternative) — one slice, one shared cell, one command, one shared context; each
746
+ alternative is a full event, and a standalone `invariant` after them binds to all.
747
+ Alternatives with no command in the slice are an error (`alternative-events-no-command`), and the same
748
+ command repeated across slices warns (`command-repeated-across-slices`).
668
749
  2. **Unknown event source** — `view X from "Event"` where the event doesn't exist anywhere.
669
750
  3. **Unknown read-model source** — `processor X from "View"` where the view doesn't exist.
670
751
  4. **Arrow endpoint mismatch** — `arrow A -> B` where A or B matches no element name.
@@ -762,9 +843,11 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
762
843
  <!-- GENERATED:validate-rules:start -- run `npm run docs:generate` to refresh, do not hand-edit -->
763
844
  | Code | Severity | Title | Fix |
764
845
  |---|---|---|---|
846
+ | `alternative-events-no-command` | error | Alternative events without a command | Add the command whose decision records one of the alternatives, in the same slice. |
765
847
  | `arrow-backward` | error | Backward arrow | Restructure so the target comes later. |
766
848
  | `arrow-unresolved-source` | error | Arrow source unresolved | Fix the arrow's source name. |
767
849
  | `arrow-unresolved-target` | error | Arrow target unresolved | Fix the arrow's target name. |
850
+ | `automation-split-by-outcome` | warning | Reaction split into one slice per outcome | Model one Automation slice recording the outcomes as alternative events — `event A | B @Context` (docs/patterns.md#automation). |
768
851
  | `binding-missing-file` | warning | Doc binding points at a missing file | Create the slice doc, or fix the `note` path. |
769
852
  | `both-ends-of-a-flow/command-no-event` | warning | Command without event | Add the event this command records. |
770
853
  | `both-ends-of-a-flow/command-untriggered` | warning | Command with no trigger | Add a `ui` or the reaction that issues it, both in this slice. |
@@ -774,6 +857,8 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
774
857
  | `both-ends-of-a-flow/ui-unbacked` | warning | `ui` with no read model or command | Add a `view` it displays, or the command it triggers. |
775
858
  | `both-ends-of-a-flow/view-unconsumed` | warning | Read model with no consumer | Add a `ui` or reaction that consumes it, or drop this instance. |
776
859
  | `code-spans-seam` | warning | Code change spans a seam | Keep one code module per model and change the two sides of a seam in separate change sets. |
860
+ | `command-repeated-across-slices` | warning | Command repeated across slices | Merge the slices: one command with several outcomes is one slice — `event A | B @Context`. |
861
+ | `command-second-intent` | warning | Command carries a second intent | Split the conditional rule set into its own command; both commands may record the same event (a bare repeat of the event is legal). |
777
862
  | `connection-legality/illegal-pair` | error | Illegal connection | Only ui→command→event→view→ui and view→reaction→command are legal — the message names the missing step. |
778
863
  | `consumer-not-adapted` | error | Consuming translation no longer matches the producer's public element | Update the translation's field block to the producer's current fields (a renamed field names its new name in the message). Additive producer changes never raise this — consumers tolerate unknown fields. |
779
864
  | `consumes-unknown-element` | error | `consumes` names no public element of that model | Point the ref at an element the producer marks `public` (`em export` lists them), or ask the producer to publish it. |
@@ -786,6 +871,8 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
786
871
  | `doc-model-element-not-in-model` | warning | Doc names an element the model doesn't have | Add the element to the model, or fix/remove it in the doc. |
787
872
  | `doc-model-field-mismatch` | warning | Doc/model field mismatch | Reconcile the field table with the model's fields — names and types. |
788
873
  | `doc-model-pattern-mismatch` | warning | Doc/model pattern mismatch | Fix the doc's `pattern:` frontmatter to match the model, or restructure the slice to match the doc. |
874
+ | `doc-names-unfed-event` | warning | Doc names an event its view does not read | Add the event to the view's `from` in the model (if the edge is real), or fix the doc's Source column / scenario. |
875
+ | `doc-omits-fed-event` | warning | Doc omits an event its view reads | Mention the event in the view's Source column (run `em slice sync`), or drop it from the view's `from`. |
789
876
  | `duplicate-element-ref` | warning | Duplicate element ref | Rename the element so its export ref is unique. |
790
877
  | `duplicate-model-key` | warning | Duplicate model key | Give each model a unique `model "Name"` so its key (and every cross-model ref) is stable. |
791
878
  | `duplicate-name` | warning | Duplicate name | Rename one of the duplicates. |
@@ -797,6 +884,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
797
884
  | `frontmatter-coherence-implemented-without-link` | warning | Implemented without a link | Add `implementedIn` once the slice ships. |
798
885
  | `frontmatter-invalid` | warning | Invalid or missing frontmatter | Add the required frontmatter keys, or add a frontmatter block. |
799
886
  | `grid-collision` | error | Band collision | Split the colliding elements into separate slices. |
887
+ | `invariant-without-scenario` | warning | Invariant cited by no scenario | Cite the INV id in the Given/When/Then (or title) of the scenario that exercises it, or add one. |
800
888
  | `invariants/declared-in-both` | warning | Invariant declared in the model and in a slice doc | Keep the model's `invariant` line; in the doc, cite the ID and elaborate instead of restating it as a `**INV-…:**` rule. |
801
889
  | `invariants/doc-cites-undeclared` | warning | Slice doc cites an undeclared invariant | Declare the ID in the model (or in a slice doc's Invariants section), or fix the citation. |
802
890
  | `invariants/duplicate-id` | error | Duplicate invariant ID | Give each model-declared invariant its own ID — a rule is declared once, on the element it guards. |
@@ -821,18 +909,27 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
821
909
  | `public-name-not-unique` | error | Two public elements of one kind share a name | Rename one of them (or drop `public` from one): a consumer's `consumes <model>:<kind>.<slug>` must name exactly one element. |
822
910
  | `reaction-from-future-view` | error | Backward timeline (reaction reads a future view) | Declare the view in or before the reaction's slice. |
823
911
  | `reaction-from-unresolved` | error | Unknown read-model source | Project the event into a view first, or fix the `from` reference. |
912
+ | `retry-slice-of` | warning | Slice only retries another command | Re-fire the same reaction via `view … again from` the retry-scheduled event; a different actor's override is its own slice with its own command (docs/patterns.md#automation). |
913
+ | `scenario-then-not-outcome` | warning | Scenario's Then names no event or rejection | Make the Then bullets name the event(s) the command records, or the rejection/error it returns. |
914
+ | `scenario-when-not-command` | warning | Scenario's When does not name the slice's command | Make the When bullet name the slice's command (for a reaction slice: the command, the reaction element, or a view of the slice) — each scenario exercises exactly one command. For a Translation, naming the translation element satisfies it. |
824
915
  | `seam-consumer-not-reaction` | error | Seam consumer is not a reaction | Point `to` at a translation/automation element (or a slice containing exactly one). |
825
916
  | `seam-crossing` | error | Change set alters a contract and its consumer together | Split the change set: land the producer's public-surface change first, then adapt the consuming model in a later change. Review on the contract file is the only override. |
826
917
  | `seam-crossing-greenfield` | warning | New public surface lands with its first consumer | Nothing to fix: a public element no consumer was bound to at the base revision is new surface, not a crossing. Later changes to it are crossings. |
827
918
  | `seam-duplicate` | warning | Duplicate seam | Remove the repeated `from`/`to` pair. |
828
919
  | `seam-endpoint-unresolved` | error | Seam endpoint does not resolve | Fix the ref to an element the named model actually exports (`em export` lists every ref), or re-declare the seam after a rename. |
829
920
  | `seam-source-not-public` | error | Seam source is not `public` | Mark the event/view `public` in its model, or point the seam at the element that is. |
921
+ | `shared-event-field-mismatch` | error | Shared event declared with different fields, tags or public | Repeat the event bare to inherit the first declaration's fields, tags and public, or make them identical. |
830
922
  | `slice-doc/structured-section-malformed` | warning | Malformed structured section | Balance the `<!-- GENERATED:em-slice-…:start -->`/`:end -->` markers (re-create the doc with `em slice new --force` if they are lost), restore the template's table header and run `em slice sync`, and give every `### Scenario:` block a Given, When and Then bullet. |
923
+ | `slice-outside-storyline` | warning | Slice outside every storyline | Move the slice inside one of the model's `storyline` blocks (advisory: storylines can be adopted incrementally). |
924
+ | `storyline-after-cycle` | error | Storyline `after` cycle | Remove an `after` clause so no storyline comes after itself. |
925
+ | `storyline-after-out-of-order` | error | Backward timeline (storyline declared above the one it comes after) | Move the storyline block below the storyline it comes `after`, or restructure the feed. |
926
+ | `storyline-after-unknown` | error | `after` names an unknown storyline | Name a storyline declared in this model, or drop the `after` clause. |
831
927
  | `system-manifest-invalid` | error | Seam manifest invalid | Fix the manifest: `systemSchemaVersion: "2.0"`, `models:` with a readable `source` per model, and nothing else (`seams:`/`owner:` moved into the models — run `em upgrade`). |
832
928
  | `system-manifest-outdated` | warning | Seam manifest is schema 1.0 | Run `em upgrade <model>.em --apply` once: it moves `seams:` into `consumes` clauses and `owner:` onto each model header, and rewrites the manifest to 2.0. |
833
929
  | `system-model-key-mismatch` | error | Manifest model key differs from the export's `model.key` | Rename the manifest's `models:` key to the computed key the message prints. |
834
930
  | `tag-composite-unknown-field` | error | Composite tag names an unknown field | Fix the field name, or add it to the event's fields. |
835
931
  | `tag-duplicate-key` | error | Duplicate tag key | Rename one of the tags so every key on the event is unique. |
932
+ | `timeline-entity-clustered` | warning | Lifecycle-per-entity shape (storyline order) | If these are create/update/archive steps on one entity, order the slices by the business journey, interleaving contexts as the story does; if they are steps of one journey, show a screen (a view) after each step. See docs/storyline.md. |
836
933
  | `translation-name-collision` | warning | Translation name reused for different producers | Use a distinct name per producer to avoid confusion. |
837
934
  | `type-cycle` | error | Cyclic type reference | Break the cycle, or route the self/mutual reference through an array. |
838
935
  | `ui-shares-slice-with-automation` | warning | `ui` shares slice with a reaction | Move the `ui` to the slice that displays the read model, or drop it. |