@milehimikey/em 1.9.1 → 1.11.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 (125) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +1 -1
  2. package/.claude/skills/event-modeling-conform/SKILL.md +5 -1
  3. package/.claude/skills/event-modeling-design/SKILL.md +31 -12
  4. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  5. package/.claude/skills/event-modeling-implement/SKILL.md +14 -3
  6. package/.claude/skills/event-modeling-implement/reference/implement.md +73 -6
  7. package/.claude/skills/event-modeling-review/SKILL.md +14 -3
  8. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +110 -11
  9. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +4 -1
  10. package/.claude/skills/event-modeling-shared/templates/constitution.md +111 -0
  11. package/.claude/skills/event-modeling-shared/templates/model-readme.md +2 -2
  12. package/.claude/skills/event-modeling-shared/templates/slice.md +4 -0
  13. package/README.md +8 -1
  14. package/dist/catalog/docJoin.d.ts +8 -0
  15. package/dist/catalog/docJoin.js +4 -0
  16. package/dist/catalog/docJoin.js.map +1 -1
  17. package/dist/catalog/sliceDoc.d.ts +8 -0
  18. package/dist/catalog/sliceDoc.js +2 -0
  19. package/dist/catalog/sliceDoc.js.map +1 -1
  20. package/dist/cli/ciInit.d.ts +10 -3
  21. package/dist/cli/ciInit.js +22 -14
  22. package/dist/cli/ciInit.js.map +1 -1
  23. package/dist/cli/ledgerCheck.d.ts +72 -0
  24. package/dist/cli/ledgerCheck.js +122 -1
  25. package/dist/cli/ledgerCheck.js.map +1 -1
  26. package/dist/cli/ratify.d.ts +37 -5
  27. package/dist/cli/ratify.js +106 -9
  28. package/dist/cli/ratify.js.map +1 -1
  29. package/dist/cli/reratify.js +14 -5
  30. package/dist/cli/reratify.js.map +1 -1
  31. package/dist/cli/review.d.ts +38 -0
  32. package/dist/cli/review.js +160 -0
  33. package/dist/cli/review.js.map +1 -0
  34. package/dist/cli/sliceIndex.d.ts +3 -0
  35. package/dist/cli/sliceIndex.js +5 -4
  36. package/dist/cli/sliceIndex.js.map +1 -1
  37. package/dist/cli/stateFile.d.ts +10 -0
  38. package/dist/cli/stateFile.js +14 -0
  39. package/dist/cli/stateFile.js.map +1 -1
  40. package/dist/cli/status.d.ts +44 -3
  41. package/dist/cli/status.js +75 -18
  42. package/dist/cli/status.js.map +1 -1
  43. package/dist/cli/systemInputs.d.ts +34 -0
  44. package/dist/cli/systemInputs.js +129 -0
  45. package/dist/cli/systemInputs.js.map +1 -0
  46. package/dist/cli.js +362 -23
  47. package/dist/cli.js.map +1 -1
  48. package/dist/emit/freshnessJson.d.ts +1 -1
  49. package/dist/emit/freshnessJson.js +5 -1
  50. package/dist/emit/freshnessJson.js.map +1 -1
  51. package/dist/emit/json.d.ts +37 -1
  52. package/dist/emit/json.js +67 -1
  53. package/dist/emit/json.js.map +1 -1
  54. package/dist/emit/ledgerJson.d.ts +1 -1
  55. package/dist/emit/ledgerJson.js +6 -1
  56. package/dist/emit/ledgerJson.js.map +1 -1
  57. package/dist/emit/queryJson.d.ts +6 -0
  58. package/dist/emit/queryJson.js +33 -0
  59. package/dist/emit/queryJson.js.map +1 -0
  60. package/dist/emit/statusJson.d.ts +1 -1
  61. package/dist/emit/statusJson.js +5 -1
  62. package/dist/emit/statusJson.js.map +1 -1
  63. package/dist/emit/systemJson.d.ts +8 -0
  64. package/dist/emit/systemJson.js +38 -0
  65. package/dist/emit/systemJson.js.map +1 -0
  66. package/dist/mcp/server.d.ts +1 -1
  67. package/dist/mcp/server.js +195 -9
  68. package/dist/mcp/server.js.map +1 -1
  69. package/dist/model/edges.d.ts +28 -1
  70. package/dist/model/edges.js +77 -15
  71. package/dist/model/edges.js.map +1 -1
  72. package/dist/model/model.d.ts +6 -0
  73. package/dist/model/model.js +2 -0
  74. package/dist/model/model.js.map +1 -1
  75. package/dist/model/qualifiedRef.d.ts +47 -0
  76. package/dist/model/qualifiedRef.js +100 -0
  77. package/dist/model/qualifiedRef.js.map +1 -0
  78. package/dist/model/queryIndex.d.ts +53 -0
  79. package/dist/model/queryIndex.js +114 -0
  80. package/dist/model/queryIndex.js.map +1 -0
  81. package/dist/model/rules.d.ts +88 -0
  82. package/dist/model/rules.js +101 -0
  83. package/dist/model/rules.js.map +1 -1
  84. package/dist/model/sliceDocIndex.d.ts +30 -0
  85. package/dist/model/sliceDocIndex.js +110 -0
  86. package/dist/model/sliceDocIndex.js.map +1 -0
  87. package/dist/model/validate.js +85 -11
  88. package/dist/model/validate.js.map +1 -1
  89. package/dist/parser/ast.d.ts +25 -0
  90. package/dist/parser/parser.d.ts +2 -0
  91. package/dist/parser/parser.js +104 -34
  92. package/dist/parser/parser.js.map +1 -1
  93. package/dist/query/format.d.ts +8 -0
  94. package/dist/query/format.js +80 -0
  95. package/dist/query/format.js.map +1 -0
  96. package/dist/query/pipeline.d.ts +17 -0
  97. package/dist/query/pipeline.js +38 -0
  98. package/dist/query/pipeline.js.map +1 -0
  99. package/dist/query/system.d.ts +60 -0
  100. package/dist/query/system.js +87 -0
  101. package/dist/query/system.js.map +1 -0
  102. package/dist/query/verbs.d.ts +100 -0
  103. package/dist/query/verbs.js +306 -0
  104. package/dist/query/verbs.js.map +1 -0
  105. package/dist/render/drawEdges.js +15 -4
  106. package/dist/render/drawEdges.js.map +1 -1
  107. package/dist/render/drawNotes.d.ts +13 -3
  108. package/dist/render/drawNotes.js +96 -20
  109. package/dist/render/drawNotes.js.map +1 -1
  110. package/dist/render/render.js +6 -5
  111. package/dist/render/render.js.map +1 -1
  112. package/dist/render/sliceDiagram.js +1 -0
  113. package/dist/render/sliceDiagram.js.map +1 -1
  114. package/dist/system/manifest.d.ts +45 -0
  115. package/dist/system/manifest.js +178 -0
  116. package/dist/system/manifest.js.map +1 -0
  117. package/dist/system/verify.d.ts +88 -0
  118. package/dist/system/verify.js +263 -0
  119. package/dist/system/verify.js.map +1 -0
  120. package/dist/templates.d.ts +14 -1
  121. package/dist/templates.js +134 -5
  122. package/dist/templates.js.map +1 -1
  123. package/dist/util/slug.js +16 -10
  124. package/dist/util/slug.js.map +1 -1
  125. package/package.json +10 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling
3
- em-version: 1.9.1
3
+ em-version: 1.11.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.9.1
3
+ em-version: 1.11.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
@@ -37,6 +37,10 @@ canonical model's names wherever the code matches them); run `em diff <model-nam
37
37
  proposals the user ratifies, then re-render and validate; run `em state set-conformance
38
38
  <revision> --report <path>` to update the state file's `Last conformance:` marker.
39
39
 
40
+ Ruling on a finding is a human gate, like ratifying a slice
41
+ (docs/process.md#the-slice-lifecycle-gates): the sweep gathers evidence and proposes, a person
42
+ decides, and `em state set-conformance` records the decision — never run it on your own judgment.
43
+
40
44
  End of phase: state file's `Last conformance:` marker updated (via `em state set-conformance`),
41
45
  Decisions log entry if any
42
46
  proposals were applied. Conform doesn't chain to another phase — it's a recurring loop, run
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-design
3
- em-version: 1.9.1
3
+ em-version: 1.11.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
@@ -36,9 +36,19 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
36
36
  directly**. Add `translation` slices for external inputs (externally triggered: external →
37
37
  translation → command → event, no slice before) and for the system reacting to its own state
38
38
  (internally triggered: read model in the slice before → translation → command → event).
39
- 2. **Elaborate scenarios — first pass (step 6).** For each slice, capture the happy-path
40
- Given/When/Then and the obvious invariants as short notes. (The full spec is the `slice` phase
41
- below.)
39
+ 2. **Elaborate scenarios — first pass (step 6).** Scaffold every slice's doc now, one per
40
+ slice, none skipped: `em slice new "<slice name>" --pattern <state-change|state-view|
41
+ automation|translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes
42
+ `slices/<slice-name>.md` at `status: draft` (the same command, flags, and `--wire`
43
+ note-binding the `slice` phase's step 2 below documents in full). Then, for each freshly
44
+ scaffolded doc, hold a first-pass Socratic pass and write the happy-path Given/When/Then into
45
+ its `## Scenarios (Given / When / Then)` section and the obvious invariants known so far into
46
+ its `## Invariants / Business Rules` section — **into the doc, never into the state file.**
47
+ Keep this pass shallow (no field tables, alternate/error flows, or NFRs yet — that's the full
48
+ deep-dive in the `slice` phase below); the docs are the home for everything collected from
49
+ here on, not yet implementation-ready specs. Because every doc already exists by the time the
50
+ `slice` phase begins, its own step 2 scaffolding call is then a no-op — that phase picks up
51
+ at its step 1's deeper pass.
42
52
  3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
43
53
  half-slice — **every command has something that triggers it**, every command emits an event,
44
54
  **every event is read by a read model**, **every read model has a consumer**, every view has a
@@ -51,13 +61,19 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
51
61
  this fact and what they do with it. The honest answers are "here's the read model we missed" or
52
62
  "nobody — so why are we recording it", and both improve the model.
53
63
 
54
- End of phase: render, update state (`em state set-phase slice`), continue with the `slice` phase
55
- below to write implementation specs.
64
+ End of phase: render, update state (`em state set-phase slice` — the mechanical marker the state
65
+ machine expects), and stop. Every slice already has a draft doc ready to receive the deep spec,
66
+ but slicing is deliberate work the team starts when it's ready for it, not this phase's automatic
67
+ next step.
56
68
 
57
69
  ## Phase: `slice` — deep slice documents
58
70
 
59
- Goal: implementation-ready specs. Go slice by slice (let the user pick order, or follow the
60
- timeline). Check `README.md`'s Slices table for what's already done (run `em slice index
71
+ Goal: implementation-ready specs. Go slice by slice **in timeline order, starting with the first
72
+ slice on the storyboard** — never ask the user which slice to spec first, and never propose
73
+ starting with whichever slice looks highest-risk or most uncertain instead. Ratification
74
+ proceeds in the same order: later slices' scenarios and invariants depend on earlier slices'
75
+ events already being settled, so working out of order means re-litigating an earlier slice's
76
+ contract mid-spec. Check `README.md`'s Slices table for what's already done (run `em slice index
61
77
  <model-name>.em` first if it looks stale).
62
78
 
63
79
  This is also where **branch / unhappy-path events** are discovered and added to the model — as a
@@ -130,10 +146,13 @@ For each slice:
130
146
  `implementedIn` once shipped). Never hand-edit the table; it's a generated block.
131
147
  6. Re-render and `em validate`.
132
148
 
133
- When a slice is **ratified** — a human flips its `status` to `ready-to-implement` with every
134
- open question resolved — it's ready to hand to an implementer. Confirm with
135
- `em validate <model>.em --slice-ready <key>`, and point whoever (or whatever) implements it
136
- at the `event-modeling-implement` skill.
149
+ A finished draft doesn't become implementable here: it goes through the review gate and then the
150
+ ratification gate, both human, both outside this phase — see
151
+ `docs/process.md#the-slice-lifecycle-gates` for who runs which and in what order.
152
+
153
+ Before the project's first slice is implemented it also needs a ratified implementation
154
+ constitution (stack, code style, testing norms, NFR baselines, review norms) — the
155
+ `event-modeling-implement` skill elicits it, see its `reference/implement.md` §7.
137
156
 
138
157
  End of phase: every slice that should exist has a doc, `README.md`'s Slices table is current,
139
158
  and the model validates clean. Suggest `event-modeling-implement` for each ratified slice.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-discover
3
- em-version: 1.9.1
3
+ em-version: 1.11.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
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-implement
3
- em-version: 1.9.1
3
+ em-version: 1.11.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
@@ -15,19 +15,30 @@ the agent builds rather than facilitates.
15
15
 
16
16
  **Read `reference/implement.md` before doing any implement work and follow it as the
17
17
  contract.** In short: gate on `em validate <model>.em --slice-ready <key>` (stop and hand
18
- back if it fails — never edit the doc to make the gate pass); treat the slice doc as the
18
+ back if it fails — never edit the doc to make the gate pass); read the project's implementation
19
+ constitution before writing code and stay inside its rules; treat the slice doc as the
19
20
  spec (read-only, except the merge-time `status`/`implementedIn` flip); surface every gap to
20
21
  the user instead of deciding it silently; cover every `INV-<MNEMONIC>-n` and every scenario with
21
22
  tests; in spec-kit projects, allocate via em-sdd-bridge (redirect mode) and never run
22
23
  `/speckit.specify`.
23
24
 
25
+ **Gate: no constitution, no first slice.** The constitution is the project's ratified house rules
26
+ — stack and architectural shape (including which implementation skill each slice pattern routes
27
+ to), code style, testing norms, NFR baselines, review/merge norms. It lives at
28
+ `.specify/memory/constitution.md` in a spec-kit project, otherwise `constitution.md` beside the
29
+ model; `em status` reports which, and whether it's there. If it's missing — or its `ratifiedBy:`
30
+ is empty, meaning nobody has signed it off — run `reference/implement.md` §7 ("Before the first
31
+ slice: the constitution") first: ask its questions conversationally, write the answers in, and
32
+ have a human ratify them. Don't decide the project's stack or style yourself to get moving.
33
+
24
34
  That contract applies to any implementing agent, whether or not the session started from
25
35
  `/event-modeling` — `em contract` prints it to stdout with no `.claude/` awareness required, for
26
36
  an agent that can run a shell but isn't Claude Code.
27
37
 
28
38
  If the project ships pattern-specific implementation skills (for example an Axon/DCB skill set
29
39
  keyed on a slice doc's `pattern:` frontmatter), route by the slice's pattern and follow those —
30
- `reference/implement.md` §2 explains where that hand-off happens.
40
+ `reference/implement.md` §2 explains where that hand-off happens, and the constitution's
41
+ routing table (§7) is what names the skill set for each pattern.
31
42
 
32
43
  Preconditions (tool check, model location) are the same as every other phase — see
33
44
  `../event-modeling-shared/reference/operating-principles.md` — but this phase additionally
@@ -41,7 +41,22 @@ implementation branch.
41
41
 
42
42
  ## 2. Read the spec
43
43
 
44
- Read `slices/<slice-key>.md` end to end — every section is load-bearing:
44
+ **First, read the project's constitution** — the house rules for *how* implementation happens
45
+ here (stack and architectural shape, which implementation skill each slice pattern routes to,
46
+ code style, testing norms, NFR baselines, review and merge norms). It lives in one of exactly two
47
+ places, never both:
48
+
49
+ - **spec-kit project** (a `.specify/` directory exists at or above the model):
50
+ `.specify/memory/constitution.md` — spec-kit's own slot, which em defers to.
51
+ - **anything else**: `constitution.md` beside the model, where `em scaffold` writes it.
52
+
53
+ `em status <model>.em --json` reports which one applies and whether it's there
54
+ (`conformance[].constitution`) — existence only; nothing in `em` reads or judges its content.
55
+ **Its rules bound every technical choice you make on this slice** (see §4). If it's missing, or
56
+ its `ratifiedBy:` is empty (spec-kit's file: the `**Ratified**:` footer still a placeholder),
57
+ it isn't ratified — go to §7 before writing code.
58
+
59
+ Then read `slices/<slice-key>.md` end to end — every section is load-bearing:
45
60
 
46
61
  | Section | What it is to the implementer |
47
62
  |---|---|
@@ -92,7 +107,10 @@ undocumented ordering, a contradiction with adjacent code. The discipline:
92
107
  a business person could have an opinion on. Silent divergence is the failure mode the whole
93
108
  conformance loop exists to catch — don't manufacture it.
94
109
  - Purely technical choices with no behavioral surface (private naming, idiomatic structure,
95
- which assertion library) are yours; anything observable in behavior, data, or contract is not.
110
+ which assertion library) are yours — **within the constitution's rules** (§2); anything
111
+ observable in behavior, data, or contract is not. If no constitution exists (or its
112
+ `ratifiedBy` is empty), surface that to the human and do not proceed on unilateral style or
113
+ stack decisions for a first slice: run the constitution step (§7) first.
96
114
 
97
115
  ## 5. Definition of done
98
116
 
@@ -104,7 +122,7 @@ undocumented ordering, a contradiction with adjacent code. The discipline:
104
122
  - Alternate/error flows (idempotency included) are covered by tests.
105
123
  - The build and full test suite are green; the model still validates (`em validate` in CI —
106
124
  you didn't touch the `.em`, so this only fails if something else broke).
107
- - Nothing between the slice doc and the code was committed as a source of truth (see §7 —
125
+ - Nothing between the slice doc and the code was committed as a source of truth (see §8 —
108
126
  work containers are ephemeral; generated or symlinked specs are renderings).
109
127
 
110
128
  ## 6. At merge: the lifecycle flip
@@ -131,7 +149,55 @@ Then, if the project keeps a model README (from `../../event-modeling-shared/tem
131
149
  link — never hand-edit that table. The `implementedIn` link is what the `conform` phase later
132
150
  uses to anchor drift-checking — leaving it empty blinds the loop.
133
151
 
134
- ## 7. Spec-kit projects: the SDD adapter
152
+ ## 7. Before the first slice: the constitution
153
+
154
+ The first time this project's slices reach implementation — or any time §2 finds no constitution,
155
+ or one whose `ratifiedBy` is still empty — **stop and run this short conversation before writing
156
+ code.** It happens once per project, not once per slice.
157
+
158
+ Start from the template the skill bundle ships,
159
+ `../../event-modeling-shared/templates/constitution.md`. Each of its five sections opens with the
160
+ question to ask; ask them **conversationally, one section at a time**, and write the answers into
161
+ the document as you go — never fill a section by guessing, by reading the codebase and inferring
162
+ "what they probably do", or by pasting the template's own examples. Unanswerable questions get
163
+ parked in the document as open items, exactly like a slice doc's Open Questions:
164
+
165
+ 1. **Stack and architectural shape** — languages/runtimes, frameworks, the architectural pattern,
166
+ where a slice's code lives, and the routing table: which implementation skill or approach each
167
+ of the four slice patterns uses (for example, State Change slices → the `axon-*` skill set).
168
+ That table is what you obey in §2 when you read a slice's `pattern:`.
169
+ 2. **Code style** — formatter/linter, naming, module layout, and what "idiomatic" means here.
170
+ 3. **Testing norms** — which test levels each pattern requires, where invariant and scenario
171
+ tests live, and the test-naming convention that lets `em coverage` trace `INV-<MNEMONIC>-n`
172
+ citations.
173
+ 4. **NFR baselines** — authz model, PII handling, performance defaults, observability: the
174
+ standing defaults every slice inherits where its own `## Non-Functional Requirements` section
175
+ is silent.
176
+ 5. **Review and merge norms** — who reviews, what blocks a merge, and who runs
177
+ `em slice mark-implemented` at merge (§6).
178
+
179
+ **A human ratifies it.** The answers are the project's, not yours — you drafted the wording, they
180
+ sign it off. In the em-native file that's the `ratifiedBy:`/`ratifiedOn:` frontmatter (a named
181
+ person and an ISO date, hand-filled — no `em` command writes this file, and an empty `ratifiedBy:`
182
+ means *draft*). In a spec-kit project it's that file's own `**Ratified**:` footer line — never add
183
+ frontmatter to spec-kit's document. Until it's ratified, treat it as a proposal: you may not
184
+ build a first slice on it.
185
+
186
+ **In a spec-kit project, merge — never duplicate.** The constitution IS
187
+ `.specify/memory/constitution.md`; em writes no second file and neither do you. Append em's five
188
+ sections to it under their own `##` headings, **leaving every existing section byte-untouched** —
189
+ including spec-kit's own `## Core Principles` and `## Governance`, and any principle a team
190
+ already wrote. If a heading of the same name is already there, add to it rather than replacing it,
191
+ and show the human the diff before writing. Two cautions: spec-kit's `/speckit.constitution`
192
+ command rewrites that whole file from its template, so run it (if at all) **before** this
193
+ conversation, never after — and never use it to author em's sections. If the file is still the
194
+ blank stock template (`[PROJECT_NAME]`, `[PRINCIPLE_1_NAME]`, … placeholders), say so: filling
195
+ spec-kit's own placeholders is the team's call, and em's sections go in alongside them either way.
196
+
197
+ `em status` will then report `constitution: present` for the model. That's the whole mechanical
198
+ check — em stores and locates this document, and never validates a word of it.
199
+
200
+ ## 8. Spec-kit projects: the SDD adapter
135
201
 
136
202
  If the repository uses spec-kit (a `.specify/` directory exists), do not hand-author spec-kit
137
203
  artifacts — allocate through **em-sdd-bridge**, redirect mode preferred:
@@ -163,7 +229,7 @@ plan/tasks as FEATURE_SPEC. The rules that keep redirect mode safe:
163
229
  No spec-kit (or any SDD tool)? Implement straight from the slice doc — it already contains
164
230
  everything a spec holds. Don't introduce an intermediate spec document of your own.
165
231
 
166
- ## 8. Never do
232
+ ## 9. Never do
167
233
 
168
234
  | Rule | Because |
169
235
  |---|---|
@@ -172,10 +238,11 @@ everything a spec holds. Don't introduce an intermediate spec document of your o
172
238
  | Never edit the `.em` model | Model edits are ratified decisions — propose, don't write |
173
239
  | Never bump `version:` | Versions move only with ratified deltas; see `em ledger` |
174
240
  | Never silently decide unspecified behavior | Silent divergence is the disease the conformance loop exists to catch |
241
+ | Never invent the house rules a first slice needs (stack, style, testing, NFR defaults) | They're the project's decision, ratified in the constitution — §7, not your judgment call |
175
242
  | Never regenerate merged code from the model | Generated-then-owned: post-merge code belongs to its owners |
176
243
  | Never commit an authored intermediate spec | The slice is the spec; anything between it and the code is a rendering |
177
244
 
178
- ## 9. Afterward: the loop closes
245
+ ## 10. Afterward: the loop closes
179
246
 
180
247
  Implementation isn't the end of the slice's story. On a cadence — or whenever someone asks —
181
248
  the `conform` phase checks implemented slices against the code, using the `implementedIn` link
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-review
3
- em-version: 1.9.1
3
+ em-version: 1.11.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
@@ -49,8 +49,19 @@ walk each one with the user, same as any open issue. Update the state file's Par
49
49
  section with who attended, and run `em state set-review <date>` to set the `Last stakeholder
50
50
  review:` marker (mirrors `Last conformance:`).
51
51
 
52
- End of phase: state file's `Last stakeholder review:` marker updated, every issue captured
53
- live triaged (resolved on the spot, moved to Open questions / parking lot, or left open on
52
+ **Per-slice outcome.** A slice whose open questions are all resolved in the room →
53
+ `em slice review <model>.em <key> --by <name>` (flips its doc to `status: reviewed` and records
54
+ `reviewedBy:`/`reviewedOn:`). Anything still open stays `draft` — the walkthrough doesn't decide
55
+ it, the next session or the open-questions list does.
56
+
57
+ **The facilitator never ratifies, never offers to ratify, and never suggests ratification in a
58
+ review session. Ratification is a separate human gate after review**
59
+ (docs/process.md#the-slice-lifecycle-gates). It is typically multi-person, routed through
60
+ CODEOWNERS on `slices/**`, and it is not this session's to run — `em slice ratify` will itself
61
+ refuse a doc that hasn't been through `em slice review`.
62
+
63
+ End of phase: state file's `Last stakeholder review:` marker updated, every walked slice whose
64
+ questions all closed moved to `status: reviewed`, every issue captured live triaged (resolved on the spot, moved to Open questions / parking lot, or left open on
54
65
  purpose). Review doesn't chain to another phase — it's a recurring, scheduled activity like
55
66
  `conform`, not a step in the discover → model → slice sequence.
56
67
 
@@ -15,7 +15,7 @@ Keep `.em` files focused on **structure**; put deep design in markdown linked vi
15
15
  em --version # print the installed em version
16
16
  em init [file] # scaffold a starter .em model
17
17
  em init [file] -f, --force # overwrite if the file exists
18
- em scaffold <name> # scaffold a full project: <slug>/<slug>.em, README.md, .event-modeling.md (see docs/cli.md — for just a starter .em, use `em init`; for a multi-model project, pass --under to nest it under a shared parent directory)
18
+ em scaffold <name> # scaffold a full project: <slug>/<slug>.em, README.md, .event-modeling.md, and constitution.md (the implementation constitution — skipped, with a note, in a spec-kit project, where `.specify/memory/constitution.md` IS that document) (see docs/cli.md — for just a starter .em, use `em init`; for a multi-model project, pass --under to nest it under a shared parent directory)
19
19
  em scaffold <name> -f, --force # overwrite the directory's contents if it already exists
20
20
  em scaffold <name> --under <dir> # parent directory to scaffold into — writes <dir>/<slug>/ instead of ./<slug>/, the supported multi-model layout (docs/cli.md, "Multi-model projects"): one directory per model, so each model's slices/ never collides with a sibling model's
21
21
  em render <file> # transpile a model and render it (or emit DOT)
@@ -52,10 +52,14 @@ em slice new <name> --wire <model-file> # also insert the
52
52
  em slice index <file> # rewrite the model's sibling README.md's GENERATED Slices table from `em export`'s slice facts (key, pattern, doc status/implementedIn) — the hand-maintained table is deprecated
53
53
  em slice index <file> --check # verify the table is current; exit non-zero on drift without writing (CI)
54
54
  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). Idempotent on the same URL; refuses to overwrite a different one; never touches `version:` or the doc body
55
- 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#what-ratified-means) that makes who ratified, and when, a first-class recorded fact. Idempotent on the same --by/--on pair; refuses to overwrite a different one already recorded; never touches `version:` or the doc body
55
+ 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
56
+ em slice review <file> <slice-key> --by <name> # the reviewer's (or facilitator's) name
57
+ em slice review <file> <slice-key> --on <date> # review date, YYYY-MM-DD (default: today)
58
+ 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. Refuses a doc that never passed the review gate (`em slice review`) unless --skip-review is passed. Idempotent on the same --by/--on pair; refuses to overwrite a different one already recorded; never touches `version:` or the doc body
56
59
  em slice ratify <file> <slice-key> --by <name> # the ratifier's name
57
60
  em slice ratify <file> <slice-key> --on <date> # ratification date, YYYY-MM-DD (default: today)
58
- em slice reratify <file> <slice-key> # bump `version:` and flip a shipped slice doc's frontmatter back to `status: ready-to-implement` — the re-ratification mechanical edit (MIL-161, mirrors `em slice mark-implemented`). Only applies to a doc at `status: implemented`; clears any stale `ratifiedBy:`/`ratifiedOn:` (they describe the PRIOR version's sign-off) so a follow-up `em slice ratify --by` applies cleanly; never touches `implementedIn:` or the doc body
61
+ em slice ratify <file> <slice-key> --skip-review # ratify without a recorded review — prints a loud notice on stderr
62
+ em slice reratify <file> <slice-key> # bump `version:` and flip a shipped slice doc's frontmatter back to `status: ready-to-implement` — the re-ratification mechanical edit (MIL-161, mirrors `em slice mark-implemented`). Only applies to a doc at `status: implemented`; clears any stale `ratifiedBy:`/`ratifiedOn:`/`reviewedBy:`/`reviewedOn:` (they describe the PRIOR version's review and sign-off) so a follow-up `em slice ratify --by` applies cleanly — and needs no fresh review; never touches `implementedIn:` or the doc body
59
63
  em changelog <file> # render a model's git history as a business-readable ledger (see docs/cli.md)
60
64
  em changelog <file> --from <rev> # start the walk at this revision (inclusive)
61
65
  em changelog <file> --to <rev> # end the walk at this revision (inclusive; default HEAD)
@@ -96,6 +100,7 @@ em migrate <file> --write # apply the rewrit
96
100
  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)
97
101
  em ledger <file> --from <rev> # baseline revision
98
102
  em ledger <file> --to <rev> # compare revision (default: current working tree)
103
+ em ledger <file> --waive <slice-key> # excuse a doc-content-without-version-bump finding for this slice key (repeatable; MIL-185, see docs/cli.md) — never waives a version regression or bump-without-content-change
99
104
  em ledger <file> --json # print a JSON document instead of the text report (see docs/cli.md)
100
105
  em coverage <file> # check that every INV-* invariant ID cited in a ready-to-implement/implemented slice doc is cited by a test under --tests <dir> (MIL-130) — mechanizes reference/implement.md's definition-of-done citation check; advisory by default, --strict for CI
101
106
  em coverage <file> --tests <dir> # directory to scan recursively for test files citing invariant IDs
@@ -111,10 +116,45 @@ em status <files> -o, --out <path> # write output to
111
116
  em freshness <file> # standalone freshness signal for one model's conformance record (MIL-164): "last conformed <rev> — N commits and M slice-PRs behind HEAD", computed from the same conform-scope machinery `em status`'s conformance clause uses — for when you want just this fact, no full state-of-the-system rollup (see docs/cli.md)
112
117
  em freshness <file> --repo <path> # git repo to compute behind-HEAD in (default: the model's own directory)
113
118
  em freshness <file> --json # print a JSON document instead of the text line
119
+ em query consumers <files> # views/reactions consuming an event, plus their slices
120
+ em query consumers <files> --event <ref-or-name> # the event's export ref or display name
121
+ em query consumers <files> --json # print a JSON document instead of the text report
122
+ em query producers <files> # commands producing an event, plus their slices and ui triggers
123
+ em query producers <files> --event <ref-or-name> # the event's export ref or display name
124
+ em query producers <files> --json # print a JSON document instead of the text report
125
+ em query downstream <files> # transitive closure along legal edges from an element — impact analysis
126
+ em query downstream <files> --of <ref-or-name> # the starting element's export ref or display name
127
+ em query downstream <files> --depth <n> # limit traversal to n hops (default: unlimited)
128
+ em query downstream <files> --json # print a JSON document instead of the text report
129
+ em query upstream <files> # transitive closure against legal-edge direction from an element
130
+ em query upstream <files> --of <ref-or-name> # the starting element's export ref or display name
131
+ em query upstream <files> --depth <n> # limit traversal to n hops (default: unlimited)
132
+ em query upstream <files> --json # print a JSON document instead of the text report
133
+ em query slices <files> # filtered slice list — pattern/status/context/persona/tag filters AND-combine
134
+ em query slices <files> --pattern <p> # state-change | state-view | automation | translation | unclassified
135
+ em query slices <files> --status <s> # the slice's joined doc status (draft, reviewed, ready-to-implement, implemented, ...)
136
+ em query slices <files> --context <c> # match a slice with an event in this @Context
137
+ em query slices <files> --persona <p> # match a slice with a ui in this @Persona
138
+ em query slices <files> --tag <t> # match a slice with an event carrying this tag key
139
+ em query slices <files> --json # print a JSON document instead of the text report
140
+ em query invariant <files> # declaring slice + doc facts for one INV-* id, and (with --tests) its test citations
141
+ em query invariant <files> --id <inv-id> # the INV-* id to look up
142
+ em query invariant <files> --tests <dir> # directory to scan for test files citing this id
143
+ em query invariant <files> --json # print a JSON document instead of the text report
144
+ em query field <files> # one field's facts on an element — type, tag/assigned markers, renamed-from chain
145
+ em query field <files> --of <element-ref> # the element's export ref or display name
146
+ em query field <files> --name <field> # the field's name
147
+ em query field <files> --json # print a JSON document instead of the text report
148
+ em query path <files> # shortest path between two elements through the six legal connection types
149
+ em query path <files> --from <ref-or-name> # the starting element's export ref or display name
150
+ em query path <files> --to <ref-or-name> # the ending element's export ref or display name
151
+ em query path <files> --json # print a JSON document instead of the text report
152
+ em system <manifest> # verify a seam manifest (system.yaml) against its models' exports — every `public` event/view bound to a reaction in another model, the cross-model half of "both ends of a flow" (MIL-194, see docs/cli.md)
153
+ em system <manifest> --json # print a JSON document instead of the text report (see docs/cli.md)
114
154
  em contract # print the packaged implementation contract (reference/implement.md) to stdout — the agent-neutral discovery path for any agent that can run a shell, not just Claude Code (MIL-129); see docs/cli.md
115
- em mcp # start an MCP (Model Context Protocol) server over stdio, exposing validate/slice_ready/list_markers/export_model/export_slice/coverage/contract as tools (MIL-21) — a structured, agent-facing alternative to shelling out to `em`; see docs/mcp.md. Equivalent to running the `em-mcp` bin directly
116
- em skill install # copy the event-modeling skill bundle into .claude/skills/ (event-modeling, event-modeling-discover/-design/-implement/-conform/-review, event-modeling-shared)
117
- em skill install -f, --force # overwrite an existing installation
155
+ em mcp # start an MCP (Model Context Protocol) server over stdio (MIL-21) — a structured, agent-facing alternative to shelling out to `em`; see docs/mcp.md for the full, current tool table (the list changes as commands gain MCP parity, so it's not repeated here — MIL-187). Equivalent to running the `em-mcp` bin directly
156
+ em skill install # copy the event-modeling skill bundle into .claude/skills/ (event-modeling, event-modeling-discover/-design/-implement/-conform/-review, event-modeling-shared); across a structural bundle change (e.g. the MIL-157 split of the old single event-modeling/ directory into this six-directory bundle), `em skill sync` is the migration path — --force now performs the same reconcile (MIL-180)
157
+ em skill install -f, --force # overwrite an existing installation, reconciling added/updated/removed files within the bundle's own managed directories the same way `em skill sync` does — never touching an unrelated sibling skill (MIL-180)
118
158
  em skill install --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
119
159
  em skill sync [path] # update the vendored .claude/skills/ event-modeling skill bundle in [path] to match the installed em package (overwrites unconditionally; local edits are never merged, MIL-93)
120
160
  em skill sync [path] --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
@@ -160,7 +200,7 @@ type Name { field: Type, ... } # named structured type, reusable from any f
160
200
  | `ui` | persona | screen / interface | `@Persona` | `note`, `issue`, `divergence`, `{ fields }` |
161
201
  | `command` | API | state-changing request | — | `note`, `issue`, `divergence`, `renamed from`, `{ fields }` |
162
202
  | `view` | API | read model / projection | — | `from "Event"…`, `note`, `issue`, `divergence`, `public`, `{ fields }` |
163
- | `event` | context | recorded fact (past tense) | `@Context` | `note`, `issue`, `divergence`, `public`, `tag`, `renamed from`, field-level `assigned`, `{ fields }` |
203
+ | `event` | context | recorded fact (past tense) | `@Context` | `note`, `issue`, `divergence`, `public`, `tag`, `renamed from`, `loops-to`, field-level `assigned`, `{ fields }` |
164
204
  | `processor` / `automation` / `saga` / `translation` | automation | system reaction / adapter | — | `from "…"`, `note`, `issue`, `divergence`, `{ fields }` |
165
205
 
166
206
  ### Clauses
@@ -185,6 +225,22 @@ type Name { field: Type, ... } # named structured type, reusable from any f
185
225
  instance are what show the view changing. `again` with no earlier declaration is a validation error.
186
226
  Use `again` (not a plain repeated `view` name) whenever the view is referenced by a
187
227
  `from`/`arrow` — plain repeats are only warning-free while unreferenced.
228
+ - **`loops-to "View"`** (events only): marks an event as re-feeding an earlier read model — a
229
+ poll/retry loop where a later fact must re-trigger an automation that already ran (a lapsed
230
+ reservation re-notifying the next person waiting in line, and similar re-triggers). Written
231
+ after the event's name/`@Context`, either on the header line or trailing an
232
+ inline `{ fields }` block; repeatable (`loops-to "A" loops-to "B"`, or comma-joined). The
233
+ target must resolve to a `view` instance STRICTLY EARLIER on the timeline — the opposite of
234
+ `from`'s "nearest at-or-before" rule; pointing at the same slice or later is a validation
235
+ error naming `from` on a later `view … again` instance as the forward-flowing alternative,
236
+ and naming a view that doesn't exist is a separate unresolved-target error. A resolved
237
+ `loops-to` counts as the event being read for both-ends-of-a-flow completeness. **No arrow is
238
+ ever drawn** for it — drawing one backward would violate the timeline laws — instead the
239
+ event gets an indigo ↩ corner marker (bottom-left) and the legend gains a "Loops" section:
240
+ `↩ 1 <Event> re-feeds "<View>"` (the number identifies the event, not the target). `em
241
+ export` carries it as `loopsTo: { name, ref }[] | null` (`from`'s shape); `model.edges` gains
242
+ `{ from: <event ref>, to: <view ref>, source: "loops-to" }`; `em query`'s adjacency labels the
243
+ connection `"loops-to"` too, so `downstream`/`upstream` follow it (cycle-safely).
188
244
  - **`note "path.md"`** on ANY element links a markdown doc. Relative to the `.em` file. Renders as
189
245
  a clickable marker in SVG and a legend entry in PNG/PDF. **This is how slice docs attach.**
190
246
  - **`issue "text"`** on ANY element flags an open question inline — the diagram-visible red
@@ -245,6 +301,25 @@ type Name { field: Type, ... } # named structured type, reusable from any f
245
301
  `=== true` convention as `tag`. **Reach for this before echoing a server-set field onto the
246
302
  command just to silence a fields-completeness warning** — that misrepresents what the client
247
303
  actually sends.
304
+ - **`derived`** (view fields only, MIL-200): marks a field as computed from which events have
305
+ landed, rather than copied from a single source event's payload (a status stepping through
306
+ states as events arrive, a computed ordinal, an availability flag two events can flip).
307
+ Trails a field's type, or the bare name of a typeless field — bare `derived` (no traced
308
+ source) or traced `derived from "Event A", "Event B"` (the two-word form parsed before the
309
+ bare check, same discipline as `renamed from` vs. plain `from`). A field whose ENTIRE text is
310
+ just `derived` is a field named `derived`, not a clause. `derived`/`derived from` on a
311
+ command/event/`ui`/`type` field is a parse error. Excludes the field from
312
+ `fields-completeness/view-field-no-source` entirely (and thus from `--slice-ready`). The
313
+ traced form still checks something: every named event must resolve among the view's ACTUAL
314
+ sources (its own `from` list, or the same-slice events for a `from`-less view) — an
315
+ unresolved name is a validation error, `derived-from-unresolved`. `em export` carries it as
316
+ `derived: boolean` on the field (always present, same `=== true` convention as `tag`/
317
+ `assigned`) and `derivedFrom: string[] | null` (the traced event name(s), `null` on a bare
318
+ `derived` field). **Reach for this instead of dropping a computed field out of the `.em` and
319
+ into doc-only prose** — the marker keeps `em export`/the model aware the field exists (the
320
+ `doc-model-field-mismatch` doc↔model check does not compare view field tables at all today —
321
+ see the slice template's field-table guidance — so this is an export-side win specifically,
322
+ not yet a doc-drift check).
248
323
  - **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
249
324
  one-per-line. Types are free text (no semantic checking) UNLESS the type string names a
250
325
  declared `type` (see Named types below), in which case it resolves to a structured
@@ -486,6 +561,14 @@ it to the slice that displays the read model, or drop it.
486
561
  a ref naming a version higher than the target's own `version:`. A `split-from`/`merged-from`
487
562
  naming a key absent from the tree is deliberately silent — that's the normal state after a
488
563
  real split/merge (see the four `lineage-*` codes in the table below).
564
+ 10. **`loops-to` not earlier, or unresolved** — an event's `loops-to "View"` naming a view at the
565
+ same slice or later (`loops-to-forward`; that's what `from` on a later `view … again`
566
+ instance is for), or naming a view that doesn't exist at all, or a name that resolves to a
567
+ non-view (`loops-to-unresolved`).
568
+ 11. **`derived from` names an unresolved event** — a view field's traced `derived from "Event
569
+ A", "Event B"` clause naming an event that isn't among the view's actual sources (its own
570
+ `from` list, or the same-slice events for a `from`-less view) — `derived-from-unresolved`.
571
+ Name one of the view's real sources, or drop the traced event.
489
572
 
490
573
  **Warnings (should fix):**
491
574
  1. **Reaction with no command** — a `processor`/`automation`/`saga`/`translation` that triggers
@@ -502,9 +585,11 @@ it to the slice that displays the read model, or drop it.
502
585
  5. **Event nobody reads** — the mirror of (4): recording an event no read model projects is a
503
586
  write with no reader. Follow every write slice with the read slice that consumes its event.
504
587
  Counts as read when a `view` names it in `from` (any `again` instance will do), when a view
505
- with no `from` of its own sits in its slice, or via an explicit `event -> view` arrow.
506
- (Events marked `public` are exempt — their reader is outside the model; same for a
507
- `public` view and the no-consumer warning.)
588
+ with no `from` of its own sits in its slice, via an explicit `event -> view` arrow, or via a
589
+ resolved `loops-to "View"` clause (a loop-back is a real consumer too — an UNRESOLVED or
590
+ forward `loops-to` does not count, so the event still warns as unread alongside its own
591
+ `loops-to-forward`/`loops-to-unresolved` error). (Events marked `public` are exempt — their
592
+ reader is outside the model; same for a `public` view and the no-consumer warning.)
508
593
  6. **Read model without source** — add `from "Event"` or place the view in a slice with an event.
509
594
  7. **Duplicate name** — the same name defined N times; references resolve to the first. Rename.
510
595
  (A duplicate `type` name always warns, unlike a duplicate element name — there's no
@@ -514,7 +599,8 @@ it to the slice that displays the read model, or drop it.
514
599
  fail while any remain open.
515
600
  9. **View field with no source** — a `view` field whose name matches no field on any instance
516
601
  of its source events. Only checked once BOTH the view and at least one source event declare
517
- `{ fields }`.
602
+ `{ fields }` — unless the field is marked `derived` (optionally `derived from "Event A",
603
+ "Event B"`, whose names must be among the view's sources), which exempts it entirely.
518
604
  10. **Event field not from a command** — an `event` field whose name matches no field on any
519
605
  command in the same slice. Only checked once BOTH the event and at least one same-slice
520
606
  command declare `{ fields }`. This is the payoff of the fields feature for slicing rigor:
@@ -561,11 +647,14 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
561
647
  | `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. |
562
648
  | `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. |
563
649
  | `cross-model-slice-doc-collision` | warning | Colliding slice doc path across models | Give each model its own directory (see docs/cli.md, "Multi-model projects"). |
650
+ | `dangling-public-event` | warning | Public event/view no seam consumes | Declare the seam that reads it, or drop `public` if nothing outside the model does. |
651
+ | `derived-from-unresolved` | error | `derived from` names an unknown event | Name one of the view's actual sources, or drop the traced event from `derived from`. |
564
652
  | `doc-model-element-not-in-doc` | warning | Model element the doc doesn't mention | Add the matching marker to the doc, or remove the element from the model. |
565
653
  | `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. |
566
654
  | `doc-model-field-mismatch` | warning | Doc/model field mismatch | Reconcile the field table with the model's fields — names and types. |
567
655
  | `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. |
568
656
  | `duplicate-element-ref` | warning | Duplicate element ref | Rename the element so its export ref is unique. |
657
+ | `duplicate-model-key` | warning | Duplicate model key | Give each model a unique `model "Name"` so its key (and every cross-model ref) is stable. |
569
658
  | `duplicate-name` | warning | Duplicate name | Rename one of the duplicates. |
570
659
  | `duplicate-slice-name` | warning | Duplicate slice name | Rename the slice so its export key is unique. |
571
660
  | `duplicate-type-name` | warning | Duplicate type name | Rename one of the duplicate `type` declarations. |
@@ -579,6 +668,8 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
579
668
  | `lineage-ref-cycle` | error | Lineage cycle | Break the cycle — a slice can't be its own ancestor. |
580
669
  | `lineage-ref-malformed` | error | Malformed lineage ref | Fix the value to `<slice-key>@v<N>`, or remove it. |
581
670
  | `lineage-version-impossible` | error | Impossible lineage version | Fix the referenced version, or ratify the target slice first. |
671
+ | `loops-to-forward` | error | `loops-to` target not earlier on the timeline | Point `loops-to` at an earlier view, or use `from` on a later `view … again` instance instead. |
672
+ | `loops-to-unresolved` | error | `loops-to` target unresolved | Name a view that exists, or declare it before this event. |
582
673
  | `note-binding-dangling` | warning | Dangling cross-slice note | Create the doc at that path, or fix/remove the note. |
583
674
  | `note-binding-extra` | warning | Extra doc-binding note, ignored | Remove the note, or point it at the slice's actual bound doc. |
584
675
  | `note-binding-unratified` | warning | Unratified cross-slice note | Add `covers: <this-slice-key>` to that doc's frontmatter, or correct the note's path. |
@@ -587,11 +678,19 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
587
678
  | `orphaned-slice-doc` | warning | Orphaned slice doc | Rename it to a current slice's key, add `covers:` (plus a `note` binding) to attach it to a live slice, or delete it. |
588
679
  | `reaction-from-future-view` | error | Backward timeline (reaction reads a future view) | Declare the view in or before the reaction's slice. |
589
680
  | `reaction-from-unresolved` | error | Unknown read-model source | Project the event into a view first, or fix the `from` reference. |
681
+ | `seam-consumer-not-reaction` | error | Seam consumer is not a reaction | Point `to` at a translation/automation element (or a slice containing exactly one). |
682
+ | `seam-duplicate` | warning | Duplicate seam | Remove the repeated `from`/`to` pair. |
683
+ | `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. |
684
+ | `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. |
685
+ | `system-manifest-invalid` | error | Seam manifest invalid | Fix the manifest: required keys, `systemSchemaVersion: "1.0"`, a readable `source` per model, and only declared model keys in seam refs. |
686
+ | `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. |
590
687
  | `tag-composite-unknown-field` | error | Composite tag names an unknown field | Fix the field name, or add it to the event's fields. |
591
688
  | `tag-duplicate-key` | error | Duplicate tag key | Rename one of the tags so every key on the event is unique. |
592
689
  | `translation-name-collision` | warning | Translation name reused for different producers | Use a distinct name per producer to avoid confusion. |
593
690
  | `type-cycle` | error | Cyclic type reference | Break the cycle, or route the self/mutual reference through an array. |
594
691
  | `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. |
692
+ | `unbound-translation` | warning | Externally-fed reaction no seam feeds | Declare the seam whose `to` is this reaction, or give it an in-model `from`. |
693
+ | `undeclared-seam-candidate` | warning | Looks connected across models, no seam declared | Declare the seam, or rename one side so the name match stops looking like a link. |
595
694
  | `view-again-without-earlier` | error | `again` without an earlier declaration | Declare the view plainly the first time it appears. |
596
695
  | `view-from-future-event` | error | Backward timeline (view reads a future event) | Move the source to a later `view X again` instance. |
597
696
  | `view-from-unresolved` | error | Unknown event source | Fix the `from` reference to name an existing event. |
@@ -56,13 +56,15 @@ to decide `frontmatter-invalid` without re-deriving frontmatter-shape rules of i
56
56
  | `schemaVersion` | integer | `1` (current dialect version) | its *value* is not read back by any `em` command today (reserved) — but its *presence* is required by `hasUsableFrontmatter()`: a doc omitting it is `frontmatter-invalid` (see below) |
57
57
  | `pattern` | string | `state-change` \| `state-view` \| `automation` \| `translation` | authored/informational only — `em catalog` and `em export` both derive pattern from the `.em` AST instead (`em export`'s `slice.pattern`, schema `1.4`) |
58
58
  | `swimlane` | string | free text, `<Persona> → <Context>` | display-only |
59
- | `status` | string, case-insensitive | `draft` \| `reviewed` \| `ready-to-implement` \| `implemented` | `em catalog`, `em render`/`em watch` header coloring; joined into `em export`'s `slice.doc.status` (schema `1.4`); paired with `implementedIn` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) |
59
+ | `status` | string, case-insensitive | `draft` \| `reviewed` \| `ready-to-implement` \| `implemented` | written by `em slice review` (→ `reviewed`, MIL-201), `em slice ratify`/`em slice reratify` (→ `ready-to-implement`) and `em slice mark-implemented` (→ `implemented`); read by `em catalog`, `em render`/`em watch` header coloring; joined into `em export`'s `slice.doc.status` (schema `1.4`); paired with `implementedIn` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) |
60
60
  | `version` | integer | positive integer, starts at `1` | joined into `em export`'s `slice.doc.version` (schema `1.4`, MIL-91) |
61
61
  | `implementedIn` | string | free text (PR/commit link) | joined into `em export`'s `slice.doc.implementedIn` (schema `1.4`, MIL-91); paired with `status` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) |
62
62
  | `split-from` | single ref | `<slice-key>@v<N>` | joined into `em export`'s `slice.doc.splitFrom` (schema `1.4`, MIL-91) and `em diff`'s `slice-added` entries (schema `1.6`, MIL-84); current-tree referential checks by `em validate` (MIL-84 — see [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage)) |
63
63
  | `merged-from` | list of refs | comma-separated `<slice-key>@v<N>, ...` | joined into `em export`'s `slice.doc.mergedFrom` (schema `1.4`, MIL-91) and `em diff`'s `slice-added` entries (schema `1.6`, MIL-84); current-tree referential checks by `em validate` (MIL-84 — see [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage)) |
64
64
  | `superseded-by` | list of refs | comma-separated `<slice-key>@v<N>, ...` | joined into `em export`'s `slice.doc.supersededBy` (schema `1.4`, MIL-91) and `em diff`'s `slice-removed` entries (schema `1.6`, MIL-84); current-tree referential checks by `em validate` (MIL-84 — see [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage)) |
65
65
  | `covers` | list of slice keys | comma-separated `<slice-key>, ...` (plain keys — **not** the `<slice-key>@v<N>` ref grammar above) | ratifies a cross-slice binding (MIL-121 — see [Cross-slice coverage](#cross-slice-coverage-covers) below): read by `em export`'s doc join, `em validate --slice-ready`, and (best-effort) `em render`/`em watch`'s Slice Status legend |
66
+ | `reviewedBy` | string | free text (typically the facilitator's or reviewer's name) | written only by `em slice review` (MIL-201 — see [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-slice-review-file-slice-key---by-name)); joined into `em export`'s `slice.doc.reviewedBy` (schema `1.11`) and `em slice index`'s Reviewed by column |
67
+ | `reviewedOn` | string | `YYYY-MM-DD` | written only by `em slice review` (MIL-201); joined into `em export`'s `slice.doc.reviewedOn` (schema `1.11`) |
66
68
  | `ratifiedBy` | string | free text (typically a person's name) | written only by `em slice ratify` (MIL-165 — see [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-slice-ratify-file-slice-key---by-name)); joined into `em export`'s `slice.doc.ratifiedBy` (schema `1.8`) and `em slice index`'s Ratified by column |
67
69
  | `ratifiedOn` | string | `YYYY-MM-DD` | written only by `em slice ratify` (MIL-165); joined into `em export`'s `slice.doc.ratifiedOn` (schema `1.8`) |
68
70
  | `owner` | string | free text (typically a person or team name) | hand-filled — no `em` command writes it; joined into `em export`'s `slice.doc.owner` (schema `1.9`, MIL-171), `em slice index`'s Owner column, and `em status`'s per-slice `owners[]` |
@@ -84,6 +86,7 @@ convention, not parser-enforced.
84
86
  | `implementedIn` | optional | optional | optional | required once the slice has *ever* reached `implemented` — may still name a **prior** version's PR during re-ratification, see below |
85
87
  | `split-from`, `merged-from`, `superseded-by` | optional in every state — present only on docs created by a split/merge, or on a doc that has been retired |
86
88
  | `covers` | optional in every state — present only on a doc that deliberately also serves another slice (MIL-121, see below); most docs never carry it |
89
+ | `reviewedBy`, `reviewedOn` | optional in every state — present once `em slice review` (MIL-201) has run at least once; a doc that never went through the review gate, or was hand-flipped to `reviewed`, simply omits both. `em slice ratify` never clears them; `em slice reratify` does (they describe the version that shipped) |
87
90
  | `ratifiedBy`, `ratifiedOn` | optional in every state — present once `em slice ratify` (MIL-165) has run at least once; a doc predating this feature, or ratified by hand, simply omits both |
88
91
  | `owner`, `tracking` | optional in every state — hand-filled whenever a team wants a who-holds-this / external-tracker link; most docs never carry either (MIL-171) |
89
92