@milehimikey/em 1.10.0 → 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 (85) 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 +64 -10
  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/dist/catalog/docJoin.d.ts +8 -0
  14. package/dist/catalog/docJoin.js +4 -0
  15. package/dist/catalog/docJoin.js.map +1 -1
  16. package/dist/catalog/sliceDoc.d.ts +8 -0
  17. package/dist/catalog/sliceDoc.js +2 -0
  18. package/dist/catalog/sliceDoc.js.map +1 -1
  19. package/dist/cli/ledgerCheck.d.ts +72 -0
  20. package/dist/cli/ledgerCheck.js +122 -1
  21. package/dist/cli/ledgerCheck.js.map +1 -1
  22. package/dist/cli/ratify.d.ts +37 -5
  23. package/dist/cli/ratify.js +106 -9
  24. package/dist/cli/ratify.js.map +1 -1
  25. package/dist/cli/reratify.js +14 -5
  26. package/dist/cli/reratify.js.map +1 -1
  27. package/dist/cli/review.d.ts +38 -0
  28. package/dist/cli/review.js +160 -0
  29. package/dist/cli/review.js.map +1 -0
  30. package/dist/cli/sliceIndex.d.ts +3 -0
  31. package/dist/cli/sliceIndex.js +5 -4
  32. package/dist/cli/sliceIndex.js.map +1 -1
  33. package/dist/cli/stateFile.d.ts +10 -0
  34. package/dist/cli/stateFile.js +14 -0
  35. package/dist/cli/stateFile.js.map +1 -1
  36. package/dist/cli/status.d.ts +44 -3
  37. package/dist/cli/status.js +75 -18
  38. package/dist/cli/status.js.map +1 -1
  39. package/dist/cli.js +142 -18
  40. package/dist/cli.js.map +1 -1
  41. package/dist/emit/freshnessJson.d.ts +1 -1
  42. package/dist/emit/freshnessJson.js +5 -1
  43. package/dist/emit/freshnessJson.js.map +1 -1
  44. package/dist/emit/json.d.ts +22 -1
  45. package/dist/emit/json.js +33 -2
  46. package/dist/emit/json.js.map +1 -1
  47. package/dist/emit/ledgerJson.d.ts +1 -1
  48. package/dist/emit/ledgerJson.js +6 -1
  49. package/dist/emit/ledgerJson.js.map +1 -1
  50. package/dist/emit/queryJson.d.ts +1 -1
  51. package/dist/emit/queryJson.js +5 -1
  52. package/dist/emit/queryJson.js.map +1 -1
  53. package/dist/emit/statusJson.d.ts +1 -1
  54. package/dist/emit/statusJson.js +5 -1
  55. package/dist/emit/statusJson.js.map +1 -1
  56. package/dist/mcp/server.js +10 -3
  57. package/dist/mcp/server.js.map +1 -1
  58. package/dist/model/edges.d.ts +18 -3
  59. package/dist/model/edges.js +34 -0
  60. package/dist/model/edges.js.map +1 -1
  61. package/dist/model/model.d.ts +4 -0
  62. package/dist/model/model.js +1 -0
  63. package/dist/model/model.js.map +1 -1
  64. package/dist/model/queryIndex.d.ts +5 -2
  65. package/dist/model/queryIndex.js +4 -1
  66. package/dist/model/queryIndex.js.map +1 -1
  67. package/dist/model/rules.d.ts +19 -0
  68. package/dist/model/rules.js +19 -0
  69. package/dist/model/rules.js.map +1 -1
  70. package/dist/model/validate.js +82 -1
  71. package/dist/model/validate.js.map +1 -1
  72. package/dist/parser/ast.d.ts +21 -0
  73. package/dist/parser/parser.js +96 -32
  74. package/dist/parser/parser.js.map +1 -1
  75. package/dist/render/drawEdges.js +7 -0
  76. package/dist/render/drawEdges.js.map +1 -1
  77. package/dist/render/drawNotes.d.ts +13 -3
  78. package/dist/render/drawNotes.js +96 -20
  79. package/dist/render/drawNotes.js.map +1 -1
  80. package/dist/render/render.js +6 -5
  81. package/dist/render/render.js.map +1 -1
  82. package/dist/templates.d.ts +14 -1
  83. package/dist/templates.js +134 -5
  84. package/dist/templates.js.map +1 -1
  85. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling
3
- em-version: 1.10.0
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.10.0
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.10.0
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.10.0
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.10.0
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.10.0
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
@@ -148,8 +153,8 @@ em system <manifest> # verify a seam ma
148
153
  em system <manifest> --json # print a JSON document instead of the text report (see docs/cli.md)
149
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
150
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
151
- em skill install # copy the event-modeling skill bundle into .claude/skills/ (event-modeling, event-modeling-discover/-design/-implement/-conform/-review, event-modeling-shared)
152
- em skill install -f, --force # overwrite an existing installation
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)
153
158
  em skill install --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
154
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)
155
160
  em skill sync [path] --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
@@ -195,7 +200,7 @@ type Name { field: Type, ... } # named structured type, reusable from any f
195
200
  | `ui` | persona | screen / interface | `@Persona` | `note`, `issue`, `divergence`, `{ fields }` |
196
201
  | `command` | API | state-changing request | — | `note`, `issue`, `divergence`, `renamed from`, `{ fields }` |
197
202
  | `view` | API | read model / projection | — | `from "Event"…`, `note`, `issue`, `divergence`, `public`, `{ fields }` |
198
- | `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 }` |
199
204
  | `processor` / `automation` / `saga` / `translation` | automation | system reaction / adapter | — | `from "…"`, `note`, `issue`, `divergence`, `{ fields }` |
200
205
 
201
206
  ### Clauses
@@ -220,6 +225,22 @@ type Name { field: Type, ... } # named structured type, reusable from any f
220
225
  instance are what show the view changing. `again` with no earlier declaration is a validation error.
221
226
  Use `again` (not a plain repeated `view` name) whenever the view is referenced by a
222
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).
223
244
  - **`note "path.md"`** on ANY element links a markdown doc. Relative to the `.em` file. Renders as
224
245
  a clickable marker in SVG and a legend entry in PNG/PDF. **This is how slice docs attach.**
225
246
  - **`issue "text"`** on ANY element flags an open question inline — the diagram-visible red
@@ -280,6 +301,25 @@ type Name { field: Type, ... } # named structured type, reusable from any f
280
301
  `=== true` convention as `tag`. **Reach for this before echoing a server-set field onto the
281
302
  command just to silence a fields-completeness warning** — that misrepresents what the client
282
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).
283
323
  - **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
284
324
  one-per-line. Types are free text (no semantic checking) UNLESS the type string names a
285
325
  declared `type` (see Named types below), in which case it resolves to a structured
@@ -521,6 +561,14 @@ it to the slice that displays the read model, or drop it.
521
561
  a ref naming a version higher than the target's own `version:`. A `split-from`/`merged-from`
522
562
  naming a key absent from the tree is deliberately silent — that's the normal state after a
523
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.
524
572
 
525
573
  **Warnings (should fix):**
526
574
  1. **Reaction with no command** — a `processor`/`automation`/`saga`/`translation` that triggers
@@ -537,9 +585,11 @@ it to the slice that displays the read model, or drop it.
537
585
  5. **Event nobody reads** — the mirror of (4): recording an event no read model projects is a
538
586
  write with no reader. Follow every write slice with the read slice that consumes its event.
539
587
  Counts as read when a `view` names it in `from` (any `again` instance will do), when a view
540
- with no `from` of its own sits in its slice, or via an explicit `event -> view` arrow.
541
- (Events marked `public` are exempt — their reader is outside the model; same for a
542
- `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.)
543
593
  6. **Read model without source** — add `from "Event"` or place the view in a slice with an event.
544
594
  7. **Duplicate name** — the same name defined N times; references resolve to the first. Rename.
545
595
  (A duplicate `type` name always warns, unlike a duplicate element name — there's no
@@ -549,7 +599,8 @@ it to the slice that displays the read model, or drop it.
549
599
  fail while any remain open.
550
600
  9. **View field with no source** — a `view` field whose name matches no field on any instance
551
601
  of its source events. Only checked once BOTH the view and at least one source event declare
552
- `{ 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.
553
604
  10. **Event field not from a command** — an `event` field whose name matches no field on any
554
605
  command in the same slice. Only checked once BOTH the event and at least one same-slice
555
606
  command declare `{ fields }`. This is the payoff of the fields feature for slicing rigor:
@@ -597,6 +648,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
597
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. |
598
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"). |
599
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`. |
600
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. |
601
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. |
602
654
  | `doc-model-field-mismatch` | warning | Doc/model field mismatch | Reconcile the field table with the model's fields — names and types. |
@@ -616,6 +668,8 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
616
668
  | `lineage-ref-cycle` | error | Lineage cycle | Break the cycle — a slice can't be its own ancestor. |
617
669
  | `lineage-ref-malformed` | error | Malformed lineage ref | Fix the value to `<slice-key>@v<N>`, or remove it. |
618
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. |
619
673
  | `note-binding-dangling` | warning | Dangling cross-slice note | Create the doc at that path, or fix/remove the note. |
620
674
  | `note-binding-extra` | warning | Extra doc-binding note, ignored | Remove the note, or point it at the slice's actual bound doc. |
621
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. |
@@ -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