@milehimikey/em 1.12.0 → 1.13.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 (105) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +4 -2
  2. package/.claude/skills/event-modeling-conform/SKILL.md +20 -9
  3. package/.claude/skills/event-modeling-conform/reference/conform.md +107 -10
  4. package/.claude/skills/event-modeling-design/SKILL.md +10 -2
  5. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  6. package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
  7. package/.claude/skills/event-modeling-implement/reference/implement.md +12 -6
  8. package/.claude/skills/event-modeling-review/SKILL.md +5 -1
  9. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +40 -2
  10. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +13 -3
  11. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +30 -0
  12. package/.claude/skills/event-modeling-shared/templates/conformance-report.md +8 -1
  13. package/.claude/skills/event-modeling-shared/templates/state.md +3 -0
  14. package/README.md +1 -0
  15. package/dist/catalog/docJoin.d.ts +9 -0
  16. package/dist/catalog/docJoin.js +6 -0
  17. package/dist/catalog/docJoin.js.map +1 -1
  18. package/dist/catalog/driftSignal.d.ts +11 -5
  19. package/dist/catalog/driftSignal.js +18 -4
  20. package/dist/catalog/driftSignal.js.map +1 -1
  21. package/dist/catalog/modelVersionValidate.d.ts +9 -0
  22. package/dist/catalog/modelVersionValidate.js +38 -0
  23. package/dist/catalog/modelVersionValidate.js.map +1 -0
  24. package/dist/catalog/sliceDoc.d.ts +12 -0
  25. package/dist/catalog/sliceDoc.js +15 -0
  26. package/dist/catalog/sliceDoc.js.map +1 -1
  27. package/dist/cli/ciInit.js +17 -0
  28. package/dist/cli/ciInit.js.map +1 -1
  29. package/dist/cli/conformScope.d.ts +1 -0
  30. package/dist/cli/conformScope.js +3 -1
  31. package/dist/cli/conformScope.js.map +1 -1
  32. package/dist/cli/conformSupersede.d.ts +24 -2
  33. package/dist/cli/conformSupersede.js +44 -3
  34. package/dist/cli/conformSupersede.js.map +1 -1
  35. package/dist/cli/findings.d.ts +134 -0
  36. package/dist/cli/findings.js +355 -0
  37. package/dist/cli/findings.js.map +1 -0
  38. package/dist/cli/metrics.d.ts +93 -0
  39. package/dist/cli/metrics.js +331 -0
  40. package/dist/cli/metrics.js.map +1 -0
  41. package/dist/cli/modelVersion.d.ts +139 -0
  42. package/dist/cli/modelVersion.js +256 -0
  43. package/dist/cli/modelVersion.js.map +1 -0
  44. package/dist/cli/reratify.d.ts +17 -0
  45. package/dist/cli/reratify.js +25 -1
  46. package/dist/cli/reratify.js.map +1 -1
  47. package/dist/cli/skillCheck.js +2 -2
  48. package/dist/cli/skillCheck.js.map +1 -1
  49. package/dist/cli/sliceConform.d.ts +50 -0
  50. package/dist/cli/sliceConform.js +206 -0
  51. package/dist/cli/sliceConform.js.map +1 -0
  52. package/dist/cli/sliceIndex.d.ts +2 -0
  53. package/dist/cli/sliceIndex.js +15 -3
  54. package/dist/cli/sliceIndex.js.map +1 -1
  55. package/dist/cli/sliceNew.d.ts +12 -0
  56. package/dist/cli/sliceNew.js +23 -0
  57. package/dist/cli/sliceNew.js.map +1 -1
  58. package/dist/cli/sliceStubAll.d.ts +76 -0
  59. package/dist/cli/sliceStubAll.js +189 -0
  60. package/dist/cli/sliceStubAll.js.map +1 -0
  61. package/dist/cli/stateFile.d.ts +63 -7
  62. package/dist/cli/stateFile.js +179 -18
  63. package/dist/cli/stateFile.js.map +1 -1
  64. package/dist/cli/status.d.ts +79 -1
  65. package/dist/cli/status.js +134 -15
  66. package/dist/cli/status.js.map +1 -1
  67. package/dist/cli/upgrade.d.ts +122 -0
  68. package/dist/cli/upgrade.js +476 -0
  69. package/dist/cli/upgrade.js.map +1 -0
  70. package/dist/cli.js +524 -36
  71. package/dist/cli.js.map +1 -1
  72. package/dist/emit/freshnessJson.d.ts +1 -1
  73. package/dist/emit/freshnessJson.js +3 -1
  74. package/dist/emit/freshnessJson.js.map +1 -1
  75. package/dist/emit/json.d.ts +11 -1
  76. package/dist/emit/json.js +35 -1
  77. package/dist/emit/json.js.map +1 -1
  78. package/dist/emit/metricsJson.d.ts +7 -0
  79. package/dist/emit/metricsJson.js +27 -0
  80. package/dist/emit/metricsJson.js.map +1 -0
  81. package/dist/emit/statusJson.d.ts +1 -1
  82. package/dist/emit/statusJson.js +17 -1
  83. package/dist/emit/statusJson.js.map +1 -1
  84. package/dist/emit/upgradeJson.d.ts +5 -0
  85. package/dist/emit/upgradeJson.js +26 -0
  86. package/dist/emit/upgradeJson.js.map +1 -0
  87. package/dist/emit/validateJson.d.ts +6 -2
  88. package/dist/emit/validateJson.js +12 -5
  89. package/dist/emit/validateJson.js.map +1 -1
  90. package/dist/mcp/server.d.ts +1 -1
  91. package/dist/mcp/server.js +114 -3
  92. package/dist/mcp/server.js.map +1 -1
  93. package/dist/model/rules.d.ts +7 -0
  94. package/dist/model/rules.js +12 -0
  95. package/dist/model/rules.js.map +1 -1
  96. package/dist/model/validate.d.ts +20 -0
  97. package/dist/model/validate.js +94 -45
  98. package/dist/model/validate.js.map +1 -1
  99. package/dist/templates.d.ts +2 -2
  100. package/dist/templates.js +7 -1
  101. package/dist/templates.js.map +1 -1
  102. package/dist/util/semver.d.ts +19 -0
  103. package/dist/util/semver.js +54 -0
  104. package/dist/util/semver.js.map +1 -0
  105. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling
3
- em-version: 1.12.0
3
+ em-version: 1.13.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
@@ -30,7 +30,9 @@ phase lives in.
30
30
  ## Routing logic
31
31
 
32
32
  1. Check the tool: `em --version`. If missing, tell the user to run `npm i -g @milehimikey/em`
33
- and stop until installed.
33
+ and stop until installed. Then check this vendored bundle's own currency against it — see
34
+ `../event-modeling-shared/reference/operating-principles.md`'s Preconditions step 2: a bundle
35
+ more than a minor version stale is a STOP, not a warning.
34
36
  2. Parse `$ARGUMENTS` for a phase name.
35
37
  - **A recognized phase name** (`discover`, `extract`, `model`, `slice`, `implement`,
36
38
  `conform`, `validate`, `watch`, `review`): look it up in the table above and invoke that
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-conform
3
- em-version: 1.12.0
3
+ em-version: 1.13.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
@@ -33,18 +33,29 @@ or doc; write the as-is picture into that scratch model (`<model-name>-asis.em`,
33
33
  canonical model's names wherever the code matches them); run `em diff <model-name>.em
34
34
  <model-name>-asis.em --json` and let `em` decide the structural deltas; classify every finding
35
35
  (real drift / model gap / internal inconsistency / uncertainty) with cited evidence; write
36
- `conformance/<date>-report.md` with proposed `issue "conformance: …"` red notes; apply only the
37
- proposals the user ratifies, then re-render and validate; run `em state set-conformance
38
- <revision> --report <path>` to update the state file's `Last conformance:` marker.
36
+ `conformance/<date>-report.md` **and its structured twin `conformance/<date>-findings.json`**
37
+ (one entry per finding, `em conform-findings check <path>` to verify) with proposed `issue
38
+ "conformance: …"` red notes; apply only the proposals the user ratifies, then re-render and
39
+ validate; run `em state set-conformance <revision> --report <path>` to update the state file's
40
+ `Last conformance:` marker.
39
41
 
40
42
  Ruling on a finding is a human gate, like ratifying a slice
41
43
  (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.
44
+ decides, and `em conform-supersede ... --locus <model|doc|code|none> --by <name>` records the
45
+ ruling on the findings record (and stamps the report) — never rule on your own judgment. Once
46
+ every finding touching a slice is ruled, `em slice conform <model-name>.em <key> --at <rev>`
47
+ certifies that version of the slice.
43
48
 
44
- End of phase: state file's `Last conformance:` marker updated (via `em state set-conformance`),
45
- Decisions log entry if any
46
- proposals were applied. Conform doesn't chain to another phase — it's a recurring loop, run
47
- again whenever the codebase has moved.
49
+ End of phase: state file's `Last conformance:` marker updated (via `em state set-conformance` —
50
+ refuses while any `implemented` slice still has an unruled finding; `--partial` escapes with a
51
+ loud notice), Decisions log entry if any proposals were applied. Conform doesn't chain to
52
+ another phase — it's a recurring loop, run again whenever the codebase has moved.
53
+
54
+ A full (non-`--partial`) `em state set-conformance` also **certifies the model's current design
55
+ version** (MIL-218, `docs/process.md#model-versions`) — it refuses if no design version has ever
56
+ been bumped ("bump a model version first") or if the model changed during this run without a
57
+ re-bump; run `em model version bump <model-name>.em --by <name>` before the final `em state
58
+ set-conformance` call if either applies.
48
59
 
49
60
  ## Phase: `validate`
50
61
 
@@ -234,10 +234,22 @@ step 2 — sorts into exactly one class:
234
234
  which finding. Cite `slice.doc.driftSignal` and `slice.doc.version` together as evidence.
235
235
  - **Extraction uncertainty** — no evidence either way. Never reported as drift or a gap.
236
236
 
237
+ **Read-model exposure (MIL-215).** Two rulings apply the classes above to a `view`'s exposure —
238
+ whether it ships a query endpoint — instead of leaving each case to fresh judgment:
239
+
240
+ - An **internal view** (no `ui` reads it, and it isn't marked `public`) implemented as
241
+ criteria-sourced state (a reaction's own read, no materialized projection — the general idiom
242
+ step 2 already names) or as an unexposed projection is **Conformant, never dead code to
243
+ flag**. It's the read operation the model draws to say an automation or translation *must*
244
+ read, not an endpoint promise; don't propose deleting it.
245
+ - An **endpoint in code on a view the model shows no `ui` reading and no `public` marking** is a
246
+ **Model gap** — "add the `ui` or remove the endpoint" — **never Real drift to delete**.
247
+ Nobody decided whether that view is exposed; deleting the code decides it for them.
248
+
237
249
  Every classification must cite the evidence recorded in step 2 (or the `em diff` entry, for
238
250
  structural findings) — a finding with no citation isn't ready to report.
239
251
 
240
- ### 5. Report + proposals
252
+ ### 5. Report + findings record + proposals
241
253
 
242
254
  Write `conformance/<YYYY-MM-DD>-report.md` in the model directory, from
243
255
  `../../event-modeling-shared/templates/conformance-report.md`. For a real-drift or model-gap finding, propose a
@@ -250,6 +262,71 @@ stays auditable) but don't ask the user to ratify what's already ratified. **You
250
262
  canonical model or a slice doc unprompted** — walk the report with the user, apply only the
251
263
  proposals they approve, then re-render and `em validate`.
252
264
 
265
+ **Also write the findings JSON (MIL-214).** Alongside the report, write
266
+ `conformance/<YYYY-MM-DD>-findings.json` — one entry per `### n.` finding, ids matching the
267
+ report's own numbering. Shape (`findingsSchemaVersion: "1.0"`, additive-only, 2-space indent,
268
+ keys sorted, findings sorted by `id`, trailing newline):
269
+
270
+ ```json
271
+ {
272
+ "findingsSchemaVersion": "1.0",
273
+ "model": "checkout.em",
274
+ "report": "conformance/YYYY-MM-DD-report.md",
275
+ "revision": "<target-repo revision>",
276
+ "findings": [
277
+ {
278
+ "id": 1,
279
+ "surface": "structural",
280
+ "class": "Real drift",
281
+ "slice": "checkout",
282
+ "evidence": "code shows X at path/to/File.kt:42",
283
+ "locus": null,
284
+ "resolvedBy": null,
285
+ "resolvedOn": null
286
+ }
287
+ ]
288
+ }
289
+ ```
290
+
291
+ `surface` is one of `structural` (`.em` <-> code — step 3's diff), `spec` (slice doc <-> code —
292
+ step 2's invariant/scenario walk), `internal` (slice doc <-> `.em` — step 2's `doc-model-*`
293
+ diagnostics), or `other`. `class` is the step-4 classification verbatim (Real drift, Model gap,
294
+ Internal inconsistency, Accepted divergence, Unpropagated delta, Extraction uncertainty). `slice`
295
+ is the concerned slice's export key, or `null` for a finding that isn't attributable to one
296
+ (counts as in-scope for every slice's unruled-findings check). `locus`/`resolvedBy`/`resolvedOn`
297
+ start `null` — a later `em conform-supersede --locus --by` call fills them in once a human rules.
298
+ Run `em conform-findings check conformance/YYYY-MM-DD-findings.json` before ending the session —
299
+ a headless run's mechanical proof it wrote a valid record.
300
+
301
+ **Ruling on a finding (MIL-214) — once the user decides.** Run, per finding or batch of findings
302
+ ruled the same way:
303
+
304
+ ```
305
+ em conform-supersede <model-name>.em conformance/YYYY-MM-DD-report.md --as-of <target-repo revision> \
306
+ --findings <spec> --locus <model|doc|code|none> --by <name>
307
+ ```
308
+
309
+ — this records `locus`/`resolvedBy`/`resolvedOn` on the named findings in the JSON (refusing to
310
+ silently overwrite an already-different `locus`) AND stamps the report's "superseded as of `<rev>`"
311
+ banner in the same call (see below). `locus: code` on an unresolved finding is the carrier for
312
+ "the spec is right, the code is wrong" — no doc delta required for that case any more; `locus:
313
+ model` means fix the `.em` (apply the proposed red note, or a stronger edit the user chose
314
+ instead); `locus: doc` means the slice doc needs a delta/re-ratification; `locus: none` covers
315
+ Accepted divergence/Unpropagated delta/Extraction uncertainty, where nothing needs to change.
316
+
317
+ **Certifying a slice, once every finding touching it is ruled (MIL-214).** For each in-scope
318
+ slice with no unruled findings left:
319
+
320
+ ```
321
+ em slice conform <model-name>.em <slice-key> --at <target-repo revision>
322
+ ```
323
+
324
+ — records `conformedVersion`/`conformedAt`/`conformedOn` on that slice's doc, so
325
+ `driftSignal` reads `in-sync` again (it reads `uncertified` for an implemented slice that's never
326
+ been through this) instead of just trusting `implementedIn`. Legal only for `status: implemented`
327
+ with a link, and itself refuses if an unruled finding for that slice still exists at this
328
+ revision — the mechanical proof this step comes only after rulings, not before.
329
+
253
330
  End of run: update the state file's `Last conformance:` marker with
254
331
 
255
332
  ```
@@ -258,14 +335,21 @@ em state set-conformance <target-repo revision> --report conformance/YYYY-MM-DD-
258
335
 
259
336
  — the revision is the one you just diffed against, so the next run's scope starts from here.
260
337
  The command writes the exact format step 1 parses back out; never hand-edit the bullet.
338
+ **Refuses (MIL-214) while any `implemented` slice still has an unruled finding** in the findings
339
+ JSON beside the report — rule on every finding first (or certify what's already ruled via `em
340
+ slice conform` above), or pass `--partial` to record the marker with a loud notice instead
341
+ (`notice: conformance marker recorded as PARTIAL — N finding(s) unruled`) when the team
342
+ deliberately wants to advance the marker without finishing every ruling this session.
261
343
  **The marker only advances with the human in the loop**: in an interactive session, run it
262
- after walking the report with the user; a headless/scheduled run writes the report but never
263
- the marker (nobody ratified the outcome), so the next run re-walks the same span — see
264
- `docs/ci.md`. If any proposals were ratified and applied, log a Decisions entry noting what
344
+ after walking the report with the user; a headless/scheduled run writes the report and findings
345
+ JSON but never the marker (nobody ratified the outcome), so the next run re-walks the same span —
346
+ see `docs/ci.md`. If any proposals were ratified and applied, log a Decisions entry noting what
265
347
  changed and why.
266
348
 
267
- **Superseding the report you just walked (MIL-164).** Once the user has ruled on this run's
268
- findings, also run:
349
+ **Superseding the report you just walked (MIL-164).** The `--locus`/`--by` call above already
350
+ stamps the report's banner in the same call when a ruling was recorded; if a finding's ruling
351
+ doesn't need `--locus`/`--by` recorded (a migration-path report predating the findings JSON, or
352
+ a ruling you're only banner-stamping after the fact), run the same command without them:
269
353
 
270
354
  ```
271
355
  em conform-supersede <model-name>.em conformance/YYYY-MM-DD-report.md --as-of <target-repo revision> --findings <spec>
@@ -298,10 +382,23 @@ each call adds its own stamp rather than overwriting the last.
298
382
  ## Completion & handoff
299
383
 
300
384
  Conform is complete when: every in-scope slice has been walked (step 2), the scratch model has
301
- been diffed (step 3), every finding is classified with cited evidence (step 4), the report is
302
- written (step 5), the state file's `Last conformance:` marker is updated, and the user has seen
303
- the report and said which proposals (if any) to apply. Applied proposals get re-rendered and
304
- re-validated before you call the run done.
385
+ been diffed (step 3), every finding is classified with cited evidence (step 4), the report AND
386
+ its findings JSON are written (step 5), the state file's `Last conformance:` marker is updated
387
+ (or deliberately left as `--partial`), and the user has seen the report and said which proposals
388
+ (if any) to apply and which findings resolve to which locus. Applied proposals get re-rendered
389
+ and re-validated before you call the run done.
390
+
391
+ A full (non-`--partial`) `em state set-conformance` does one more thing (MIL-218): it also
392
+ **certifies** the model's current design version — writes `certified: { at, on, report,
393
+ findings }` into that version's `model-versions/v<N>.json` manifest, and rewrites the state
394
+ file's `Certified:` bullet. This is the "conform ran clean against version N" fact a
395
+ stakeholder or a release note can point at. It only works on an already-**bumped** design
396
+ version — `em state set-conformance` refuses (pointing at `em model version bump`) if no
397
+ manifest exists yet, or if the model has drifted since the last bump (the certification would
398
+ otherwise name a version that isn't what conform actually walked). If the model changed during
399
+ this conform run (a slice's `version:` moved, or the `.em` file itself changed), bump a new
400
+ design version (`em model version bump --by <name>`) before the final `em state
401
+ set-conformance` call. `--partial` never certifies. See `docs/model-versions.md`.
305
402
 
306
403
  Conform doesn't chain to another phase automatically — it's a recurring loop, not a step in
307
404
  building the model. Suggest running it again next time the target codebase has moved, at
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-design
3
- em-version: 1.12.0
3
+ em-version: 1.13.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
@@ -51,7 +51,11 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
51
51
  deep-dive in the `slice` phase below); the docs are the home for everything collected from
52
52
  here on, not yet implementation-ready specs. Because every doc already exists by the time the
53
53
  `slice` phase begins, its own step 2 scaffolding call is then a no-op — that phase picks up
54
- at its step 1's deeper pass.
54
+ at its step 1's deeper pass. For a purely exploratory/backbone-mapping model that only needs
55
+ status coloring right now (not a full spec per slice yet), `em slice stub-all <model>.em` is
56
+ the one-command fast path: a near-free stub per undocumented slice, wired, in slice order
57
+ (MIL-184) — skip the per-slice `em slice new` calls above and deepen individual docs later
58
+ when the team is ready.
55
59
  3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
56
60
  half-slice — **every command has something that triggers it**, every command emits an event,
57
61
  **every event is read by a read model**, **every read model has a consumer**, every view has a
@@ -63,6 +67,10 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
63
67
  For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
64
68
  this fact and what they do with it. The honest answers are "here's the read model we missed" or
65
69
  "nobody — so why are we recording it", and both improve the model.
70
+ **Per `view`, also ask who reads it (MIL-215):** a person or client reads it → draw the `ui`
71
+ that reads it; another model/system reads it → mark it `public`; only an automation reads
72
+ it → nothing, it's internal by design. No new keyword — reachability is already what `ui` and
73
+ `public` mean.
66
74
 
67
75
  End of phase: render, update state (`em state set-phase slice` — the mechanical marker the state
68
76
  machine expects), and stop. Every slice already has a draft doc ready to receive the deep spec,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-discover
3
- em-version: 1.12.0
3
+ em-version: 1.13.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.12.0
3
+ em-version: 1.13.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
@@ -35,9 +35,10 @@ every `## Open Questions` checkbox is checked, and no status/version/link incohe
35
35
  `statusReady`/`noUncheckedOpenQuestions`) if you need to say which one is blocking.
36
36
 
37
37
  **`ready: false` means stop** — with one named exception: a slice already `status: implemented`
38
- with `driftSignal: in-sync` also reads `ready: false` (its `statusReady` gate fails because
39
- nothing is newly ratified), and that specific case is re-implementation/repair, not a blocker —
40
- see §3's third mode before treating it as one. For every other `false`, report which `gates`
38
+ with `driftSignal: in-sync` or `uncertified` (MIL-214) also reads `ready: false` (its
39
+ `statusReady` gate fails because nothing is newly ratified), and that specific case is
40
+ re-implementation/repair, not a blocker — see §3's third mode before treating it as one. For
41
+ every other `false`, report which `gates`
41
42
  entries are `false` (and any `diagnostics` entries concerning this slice) and hand the slice
42
43
  back to the humans. Never make the gate pass yourself — checking an open-question box, flipping
43
44
  `status`, or editing frontmatter are ratification decisions, and ratification happens in a
@@ -86,7 +87,9 @@ section — and the routing from it is mechanical, not a technical choice you ma
86
87
  translation, a view read only by a reaction) means internal-only: no endpoint. If one seems
87
88
  needed anyway, that's a gap to surface (§4), not a technical choice to make mid-implementation
88
89
  — adding a route the model doesn't show is exactly the kind of silent divergence §4 exists to
89
- catch.
90
+ catch. An endpoint on a view the model shows no `ui` reading is a gap to surface the same way
91
+ (MIL-215) — conform will classify it as a **model gap**, never drift to delete, once you flag
92
+ it (`../../event-modeling-conform/reference/conform.md`, Flow step 4).
90
93
  - **`public` events and views** are the model's published integration surface: their shape is a
91
94
  contract, not an implementation detail. `em validate --list-public` lists them; `em typespec`
92
95
  is the POC generator for a downstream schema. Changing a public element's shape is a ratified
@@ -122,8 +125,11 @@ Check the doc's `version:` and `## Delta` section (or `em export`'s `slice.doc.d
122
125
  the people who've edited it (generated-then-owned); regeneration is legal only for slices
123
126
  whose code never merged.
124
127
  - **Re-implementation / repair** (`status: implemented`, `implementedIn` set, `driftSignal:
125
- in-sync` — `--slice-ready` reports `ready: false` purely because `statusReady` fails, with no
126
- other gate false and no doc content changed): the doc hasn't changed, but the shipped code
128
+ in-sync` **or** `uncertified` (MIL-214 — certification status is orthogonal to whether the doc
129
+ changed; most shipped slices sit at `uncertified` until a conform sweep certifies them, which
130
+ doesn't change which mode this is) — `--slice-ready` reports `ready: false` purely because
131
+ `statusReady` fails, with no other gate false and no doc content changed): the doc hasn't
132
+ changed, but the shipped code
127
133
  needs rebuilding or fixing — deleted code, a reverted PR, a bug in behavior the doc already
128
134
  specifies. **The doc is still the spec.** The gate's `false` here means "nothing new is
129
135
  ratified," not "stop" — it is not license to self-initiate: proceed only when a human has
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: event-modeling-review
3
- em-version: 1.12.0
3
+ em-version: 1.13.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
@@ -39,6 +39,10 @@ screen. Use Prev/Next (or the left/right arrow keys) to step through slices in d
39
39
  order — each one pans/zooms into view with everything else dimmed, so the room's attention
40
40
  tracks one slice at a time.
41
41
 
42
+ **When a State View slice is on screen, ask who reads it (MIL-215):** a person or client in the
43
+ room → the `ui` should draw it; another system → `public`; only an automation → nothing, it's
44
+ internal by design and needs no endpoint.
45
+
42
46
  **Live capture:** when a stakeholder raises something unresolved, add `issue "..."` to the
43
47
  relevant element in the `.em` file and save — exactly the same mechanism `event-modeling-conform`
44
48
  uses for any other issue. The browser updates over the existing SSE push within moments, without
@@ -49,6 +49,12 @@ em slice new <name> --pattern <pattern> # slice pattern: s
49
49
  em slice new <name> --swimlane <swimlane> # swimlane, e.g. "Persona → Context"
50
50
  em slice new <name> -f, --force # overwrite the file if it already exists
51
51
  em slice new <name> --wire <model-file> # also insert the `note "slices/<key>.md"` line onto the slice's primary element in this .em file (matched by export key), instead of just printing it to paste by hand (MIL-161)
52
+ em slice new <name> --stub # write a near-free stub instead: same 5 frontmatter keys, but a one-line placeholder body instead of the diagram-image stub and every judgment section (MIL-184) — deepen it later by re-running without --stub and -f
53
+ em slice stub-all <file> # scaffold + wire a near-free stub (`em slice new --stub`'s content, MIL-184) for every slice in <file> with no resolvable doc — the fast path to status coloring for an exploratory/backbone model without hand-running `slice new` per slice. Skips a continuation slice (MIL-208, it has no doc of its own), an already-documented slice, and a slice whose pattern can't be classified
54
+ em slice stub-all <file> --status <status> # target status for every stub: draft | reviewed | ready-to-implement | implemented
55
+ em slice stub-all <file> --by <name> # identity for reviewedBy/ratifiedBy — required unless --status draft
56
+ em slice stub-all <file> --implemented-in <url> # PR/commit URL for implementedIn — required with --status implemented
57
+ em slice stub-all <file> --dry-run # list what would be stubbed/wired without writing anything
52
58
  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
59
  em slice index <file> --check # verify the table is current; exit non-zero on drift without writing (CI)
54
60
  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
@@ -60,6 +66,10 @@ em slice ratify <file> <slice-key> --by <name> # the ratifier's n
60
66
  em slice ratify <file> <slice-key> --on <date> # ratification date, YYYY-MM-DD (default: today, local date)
61
67
  em slice ratify <file> <slice-key> --skip-review # ratify without a recorded review — prints a loud notice on stderr
62
68
  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
69
+ em slice conform <file> <slice-key> # record per-slice-per-version conformance certification: sets `conformedVersion:`/`conformedAt:`/`conformedOn:` on a slice doc (MIL-214) — the fact `driftSignal: in-sync` now depends on. Legal only for `status: implemented` with an `implementedIn:` link. Idempotent on the same (version, --at) pair; a different --at simply overwrites (a later re-certification is normal — there's no way for em to tell 'later' from 'earlier' for an arbitrary revision string, so there's no --force to reach for). Refuses when the newest conformance/*-findings.json matching --at still has an unruled finding in scope for this slice; --skip-findings-check overrides with a loud notice.
70
+ em slice conform <file> <slice-key> --at <rev> # the target-repo revision this certification sweep diffed against
71
+ em slice conform <file> <slice-key> --on <date> # certification date, YYYY-MM-DD (default: today, local date)
72
+ em slice conform <file> <slice-key> --skip-findings-check # certify even with unruled findings in scope — prints a loud notice on stderr
63
73
  em changelog <file> # render a model's git history as a business-readable ledger (see docs/cli.md)
64
74
  em changelog <file> --from <rev> # start the walk at this revision (inclusive)
65
75
  em changelog <file> --to <rev> # end the walk at this revision (inclusive; default HEAD)
@@ -67,21 +77,32 @@ em changelog <file> -o, --out <path> # write to a file
67
77
  em state read [dir] # print the state file's mechanical fields as JSON
68
78
  em state set-phase <phase> [dir] # rewrite Current phase: (and Last updated:); --step also rewrites Current step:
69
79
  em state set-phase <phase> [dir] --step <n> # also set Current step: to this value
70
- em state set-conformance <revision> [dir] # rewrite Last conformance: (and Last updated:) in the exact format reference/conform.md parses
80
+ em state set-conformance <revision> [dir] # rewrite Last conformance: (and Last updated:) in the exact format reference/conform.md parses. Refuses (MIL-214) while any `implemented` slice has an unruled conformance finding in the findings JSON beside --report (a `slice: null` finding blocks every slice); --partial records the marker with a ` (partial)` suffix instead, with a loud notice. Without a findings JSON beside --report: warns once and records the marker as given (migration path for a report predating `em conform-findings`)
71
81
  em state set-conformance <revision> [dir] --report <path> # path to the conformance report just written
82
+ em state set-conformance <revision> [dir] --partial # record the marker as PARTIAL even though some in-scope findings are still unruled
72
83
  em state set-review <date> [dir] # rewrite Last stakeholder review: (and Last updated:)
73
84
  em state log-usage <file> # append one Usage log line — phase(s) touched + em validate's diagnostic categories hit, deduped and canonically formatted (MIL-161, docs/usage-data.md) — the mechanical half of 'save state at the end of every session' that used to be run-validate-then-hand-format; state file resolved next to <file>, same convention as em conform-scope
74
85
  em state log-usage <file> --phases <list> # comma-separated phase(s) touched this session: discover, extract, model, slice, implement, conform, review, validate, watch
86
+ em model version bump <file> # bump the model's design version: write model-versions/v<N+1>.json (sibling of slices/, conformance/) and rewrite the state file's `Model version:` bullet (MIL-218). Refuses when the state file is missing (run `em scaffold` first), when --by is empty, and when nothing has changed since the current version (same model content hash AND same slice-version vector) unless --force — a no-op bump is the one case --force is right for.
87
+ em model version bump <file> --by <name> # the bumper's name
88
+ em model version bump <file> --on <date> # bump date, YYYY-MM-DD (default: today, local date)
89
+ em model version bump <file> --force # bump even though nothing has changed since the current version
90
+ em model version show <file> # print the model's current design version and the most recently certified version, if any (model-versions/*.json — MIL-218)
91
+ em model version show <file> --json # print a JSON document instead of text
75
92
  em usage-report [root] # aggregate every .event-modeling.md's Usage log under [root] into phase/diagnostic-category tallies (MIL-161) — replaces docs/usage-data.md's hand-rolled grep/awk/sort pipeline; a logged line that doesn't match the canonical em state log-usage format is reported under unparseableLines rather than silently mistallied or dropped
76
93
  em usage-report [root] --json # print a JSON document instead of the text report
77
94
  em conform-scope <file> # mechanize conform phase step 1 (reference/conform.md): map the target repo's changed paths since Last conformance: to slices via each slice doc's implementedIn, JSON to stdout — --seed-asis also seeds the <model>-asis.em scratch model (see docs/cli.md)
78
95
  em conform-scope <file> --repo <path> # path to (or inside) the target codebase's git repository
79
96
  em conform-scope <file> --full # ignore Last conformance:/changed paths; scope every implemented slice
80
97
  em conform-scope <file> --seed-asis # write <model>-asis.em as a byte copy of the canonical model and ensure it's gitignored
81
- em conform-supersede <file> <report-path> # stamp a conformance report with a "superseded as of <rev>" banner once its findings have been ruled on (MIL-164, docs/process.md#what-ratified-means) — the companion step to run at ratification time so a reader following the report's file:line citations knows they describe an ancestor of the current model. Additive-only splice, never a rewrite of the report; idempotent on the same --as-of/--findings/--on stamp; refuses if the report doesn't exist
98
+ em conform-supersede <file> <report-path> # stamp a conformance report with a "superseded as of <rev>" banner once its findings have been ruled on (MIL-164, docs/process.md#what-ratified-means) — the companion step to run at ratification time so a reader following the report's file:line citations knows they describe an ancestor of the current model. Additive-only splice, never a rewrite of the report; idempotent on the same --as-of/--findings/--on stamp; refuses if the report doesn't exist. --locus/--by (MIL-214, both required together) additionally record the ruling itself — locus/resolvedBy/resolvedOn — on the findings named by --findings in the sibling conformance/<date>-findings.json, when one exists (warns once and stamps the banner only when it doesn't — the migration path for a report predating em conform-findings)
82
99
  em conform-supersede <file> <report-path> --as-of <rev> # the revision this ruling was made against — same value passed to `em state set-conformance`
83
100
  em conform-supersede <file> <report-path> --findings <spec> # which finding number(s) this stamps as ruled, e.g. "1-3" or "1,2,4"
84
101
  em conform-supersede <file> <report-path> --on <date> # ruling date, YYYY-MM-DD (default: today, local date)
102
+ em conform-supersede <file> <report-path> --locus <locus> # who/what the named finding(s) say is wrong: model | doc | code | none (requires --by)
103
+ em conform-supersede <file> <report-path> --by <name> # who ruled on the named finding(s) (requires --locus)
104
+ em conform-findings check <path> # shape-validate a conformance/<date>-findings.json file (findingsSchemaVersion, required fields, enum values, ids sorted+unique, a ruled finding carries resolvedBy+resolvedOn) — exit 1 on shape errors. The conform skill writes this file directly alongside its report; this is how a headless run verifies what it just wrote.
105
+ em conform-findings check <path> --json # print a JSON document instead of the text report
85
106
  em watch <file> # re-render on every save
86
107
  em watch <file> -o, --out <path> # output path (extension picks the format)
87
108
  em watch <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
@@ -117,6 +138,10 @@ em status <files> -o, --out <path> # write output to
117
138
  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)
118
139
  em freshness <file> --repo <path> # git repo to compute behind-HEAD in (default: the model's own directory)
119
140
  em freshness <file> --json # print a JSON document instead of the text line
141
+ em metrics <file> # the pilot metrics named in advance by the register, computed from git history alone (MIL-170): ratification turnaround (reviewed -> ratified -> implemented), conform-cycle cadence + finding counts, and status-vs-reality disagreement over time — plus a fourth, reported as not computable from history (see docs/usage-data.md). `<file>` is an anchor .em file, used only to locate slices/, conformance/, and .event-modeling.md relative to it — same convention as em ledger; never parsed or compiled
142
+ em metrics <file> --from <rev> # baseline revision
143
+ em metrics <file> --to <rev> # compare revision (default: HEAD)
144
+ em metrics <file> --json # print a JSON document instead of the text report (see docs/cli.md)
120
145
  em query consumers <files> # views/reactions consuming an event, plus their slices
121
146
  em query consumers <files> --event <ref-or-name> # the event's export ref or display name
122
147
  em query consumers <files> --json # print a JSON document instead of the text report
@@ -165,6 +190,10 @@ em ci init <model> # install .github/
165
190
  em ci init <model> --tests <dir> # test directory the coverage/status steps scan for INV-* citations
166
191
  em ci init <model> -f, --force # replace an existing workflow file that has no GENERATED markers
167
192
  em ci init <model> --check # verify both files match the current preset; exit non-zero on drift without writing (CI)
193
+ em upgrade <file> # bring a model repo authored under an older em (1.6 forward) up to the installed version: an ordered list of mechanical steps (skill bundle, reaction shape, state-file bullets, generated CI blocks, constitution scaffold) plus a human list of things no command can safely decide by itself (MIL-219, see docs/upgrading.md). Dry-run by default; --apply makes one git commit per applicable step, refusing on a dirty working tree
194
+ em upgrade <file> --apply # apply every applicable mechanical step, one git commit each, then a final `Em version:` commit
195
+ em upgrade <file> --check # exit non-zero only on a hard incompatibility (unparseable state file, an un-migratable .em shape) — writes nothing; what CI runs
196
+ em upgrade <file> --json # print a JSON document instead of text (dry-run/--check only, never with --apply)
168
197
  ```
169
198
  <!-- GENERATED:cli:end -->
170
199
 
@@ -270,6 +299,14 @@ type Name { field: Type, ... } # named structured type, reusable from any f
270
299
  this model). `em export` carries it as `public: true`/`false`; `em diff` reports a flip as
271
300
  `event marked public`/`event unmarked public` (the entry cites the element's kind, so a
272
301
  view flip reads `view "X"`); `em validate --list-public` audits the whole public surface.
302
+ **Exposure of a read model (MIL-215) is three cases, no dedicated keyword:** a person/client
303
+ reads it → draw the `ui` that reads it (ships the endpoint); another model/system reads it →
304
+ `public` (a cross-model contract, verified by `em system <manifest>`, not `em validate`); only
305
+ an automation reads it → nothing — internal by design, the read a reaction is required to
306
+ make, not an endpoint. `em validate --list-public` flags a `public` view with no `ui`/reaction
307
+ reading it anywhere in this model as an audit note ("no in-model reader"), not a warning — a
308
+ cross-model `public` view's reader legitimately lives outside this file (see "`em validate`
309
+ rules" below).
273
310
  - **`tag`** (events only): declares a DCB (Dynamic Consistency Boundary) tag key. Three forms:
274
311
  a trailing `tag` on a field line inside the event's `{ … }` block (`priceId: UUID tag`, or
275
312
  typeless `priceId tag`) — an identity tag, key defaults to the field's own name; a bare field
@@ -681,6 +718,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
681
718
  | `lineage-version-impossible` | error | Impossible lineage version | Fix the referenced version, or ratify the target slice first. |
682
719
  | `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. |
683
720
  | `loops-to-unresolved` | error | `loops-to` target unresolved | Name a view that exists, or declare it before this event. |
721
+ | `model-version-stale` | warning | Model version stale | Run `em model version bump` to record the current model content/slice-version vector. |
684
722
  | `note-binding-dangling` | warning | Dangling cross-slice note | Create the doc at that path, or fix/remove the note. |
685
723
  | `note-binding-extra` | warning | Extra doc-binding note, ignored | Remove the note, or point it at the slice's actual bound doc. |
686
724
  | `note-binding-unratified` | warning | Unratified cross-slice note | Add `covers: <this-slice-key>` to that doc's frontmatter, or correct the note's path. |
@@ -77,7 +77,17 @@ right here as `reference/slice-doc-schema.md` (kept in sync with the em repo's
77
77
 
78
78
  1. Check the tool: `em --version`. If missing, tell the user to run `npm i -g @milehimikey/em`
79
79
  and stop until installed.
80
- 2. Locate the model. Look for an existing `<dir>/.event-modeling.md` and `*.em` in the working
80
+ 2. Check this vendored skill bundle's own currency (MIL-219): read this file's `em-version:`
81
+ frontmatter stamp (or any sibling skill's SKILL.md — they're stamped together) and compare it
82
+ to `em --version` from step 1. **If the installed em is ahead by a minor version or more
83
+ (1.9.x → 1.11.x, or any major bump), STOP** — don't proceed with discover/model/slice/
84
+ implement/conform/review work on this stamp. Tell the user the vendored bundle is stale and to
85
+ run `em skill sync` (refresh the bundle only) or `em upgrade` (the bundle plus the model
86
+ repo's other mechanical migrations, docs/upgrading.md) before continuing. A patch-only gap
87
+ (1.11.2 → 1.11.4) is never worth stopping over — proceed normally. This matters because a
88
+ stale bundle silently follows old rules: an agent reading superseded preconditions/gates
89
+ can hand back work that looks right by last week's contract and wrong by this week's.
90
+ 3. Locate the model. Look for an existing `<dir>/.event-modeling.md` and `*.em` in the working
81
91
  directory, or — for a multi-model project laid out per the convention below — one level down
82
92
  inside a `models/` subfolder (each model gets its own `models/<slug>/` directory; check each
83
93
  candidate for its own `.event-modeling.md`/`*.em` pair). If found, run `em state read <dir>`
@@ -85,12 +95,12 @@ right here as `reference/slice-doc-schema.md` (kept in sync with the em repo's
85
95
  conformance, last review) as JSON — don't parse the bullets by hand. If not, and the phase
86
96
  needs one, ask the user for the model name and where to create it (a lone model goes at the
87
97
  project root; one of several goes under `models/`, per "Multi-model projects" below).
88
- 3. If you're not sure which phase applies, or the user gave no phase, use `em state read <dir>`'s
98
+ 4. If you're not sure which phase applies, or the user gave no phase, use `em state read <dir>`'s
89
99
  `phase`/`step` to resume the recorded phase/step — or invoke the `event-modeling` skill, which
90
100
  does exactly this and routes to the right skill. If no model exists, propose starting
91
101
  `discover` (greenfield, `event-modeling-discover`) or `extract` (modeling an existing system,
92
102
  also `event-modeling-discover`).
93
- 4. Populate the state file's Participants section at session start. For a live workshop, ask for
103
+ 5. Populate the state file's Participants section at session start. For a live workshop, ask for
94
104
  a single human proxy to relay questions to the room, and attribute every answer/decision in
95
105
  the Decisions log to a named participant.
96
106
 
@@ -69,6 +69,9 @@ to decide `frontmatter-invalid` without re-deriving frontmatter-shape rules of i
69
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`) |
70
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[]` |
71
71
  | `tracking` | string | free text (typically an external ticket/issue URL) | hand-filled — no `em` command writes it; joined into `em export`'s `slice.doc.tracking` (schema `1.9`, MIL-171) and `em slice index`'s Tracking column. This is the exact field `em-tracker-bridge` reads to find the ticket mirroring this slice — `em` only stores and displays it, it never talks to a tracker itself |
72
+ | `conformedVersion` | integer | positive integer | written only by `em slice conform` (MIL-214 — see [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-slice-conform-file-slice-key---at-rev)): this slice's `version:` at the time a conform sweep last certified it. Joined into `em export`'s `slice.doc.conformedVersion` (schema `1.13`); paired with `version` to compute `slice.doc.driftSignal`'s `"uncertified"` case |
73
+ | `conformedAt` | string | free text (a target-repo revision, NOT a `<slice-key>@v<N>` lineage ref) | written only by `em slice conform` (MIL-214): the revision that certification sweep diffed against. Joined into `em export`'s `slice.doc.conformedAt` (schema `1.13`) and `em slice index`'s Conformed column |
74
+ | `conformedOn` | string | `YYYY-MM-DD` | written only by `em slice conform` (MIL-214). Joined into `em export`'s `slice.doc.conformedOn` (schema `1.13`) |
72
75
 
73
76
  Five keys — `schemaVersion`/`pattern`/`swimlane`/`status`/`version` — are what
74
77
  `hasUsableFrontmatter()` requires: a doc omitting any of them is `frontmatter-invalid` to
@@ -89,6 +92,7 @@ convention, not parser-enforced.
89
92
  | `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) |
90
93
  | `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 |
91
94
  | `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) |
95
+ | `conformedVersion`, `conformedAt`, `conformedOn` | optional in every state — present once `em slice conform` (MIL-214) has run at least once for the CURRENT version; a doc that's never been through a conform sweep, or whose `version` has since bumped past what was certified, simply has none of the three (or a stale triple — see `driftSignal`'s `uncertified` case below) |
92
96
 
93
97
  `em export`'s slice-doc join (MIL-91) is the first `em` command to mechanically check the
94
98
  required row above: a bound doc missing any of `schemaVersion`/`pattern`/`swimlane`/`status`/
@@ -101,6 +105,32 @@ No `em` command fails a build over this table. `em validate`'s frontmatter-coher
101
105
  (never fails) on the one combination checkable without git history: `status: implemented` with
102
106
  no `implementedIn` link at all.
103
107
 
108
+ ## Stub docs
109
+
110
+ `em slice new --stub` and `em slice stub-all` (MIL-184, ruled 2026-09-08 on GH #128 — see that
111
+ issue for the fuller design rationale) write a **stub**: the exact same 5 required-at-`status:
112
+ draft` frontmatter keys above, but a one-line placeholder body (`_Stub — deepen with the slice
113
+ phase (see slice-doc-schema.md)._`) instead of the diagram-image stub and every judgment section
114
+ (Intent, Command, Scenarios, Open Questions, …) a full slice doc eventually carries.
115
+
116
+ A stub is not a lesser doc, frontmatter-wise — it's a real doc with a real `status`, parsed and
117
+ joined by `em export`/`em catalog`/`em status`/`em slice index` exactly like a fully-written one,
118
+ because it's the same frontmatter dialect. The doc stays the single source of truth for
119
+ `status`; a stub is just the cheapest possible way to give a slice one before anyone's ready to
120
+ write the real spec.
121
+
122
+ **A stub CAN pass `em validate --slice-ready`.** That gate checks four things (`computeSliceReadyGates`,
123
+ [validation.md#slice-readiness](https://github.com/milehimikey/em/blob/main/docs/validation.md#slice-readiness)): a doc is bound, its frontmatter
124
+ is usable, `status` is `ready-to-implement`, and its Open Questions count has none unchecked. A
125
+ stub with no `## Open Questions` section at all has `openQuestionsTotal: 0` (see
126
+ [Open Questions section: lifecycle](#open-questions-section-lifecycle) below) — zero unchecked is
127
+ vacuously true — so a stub escalated to `ready-to-implement` (`em slice stub-all --status
128
+ ready-to-implement --by <name>`) passes cleanly. The readiness gate is frontmatter-plus-checkbox-
129
+ count only; it has no opinion about whether Intent/Command/Scenarios/Invariants were ever
130
+ written. Demanding that content is the `slice` phase's (and, for a live walkthrough, the review
131
+ skill's) job, not a machine gate's — see `../.claude/skills/event-modeling-design/SKILL.md` and
132
+ `../.claude/skills/event-modeling-review/SKILL.md`.
133
+
104
134
  ## Lineage: grammar and cardinality
105
135
 
106
136
  Grammar: `<slice-key>@v<N>` — `<slice-key>` is the referenced slice's kebab-case filename stem
@@ -31,7 +31,14 @@ Replace bracketed placeholders; delete guidance comments before finishing.
31
31
  ## Findings
32
32
 
33
33
  <!-- One subsection per finding. Group however reads clearest (by slice, or by surface) but keep
34
- every field below for every finding. Order doesn't matter; classification does. -->
34
+ every field below for every finding. Order doesn't matter; classification does.
35
+
36
+ MIL-214: this section's structured twin is `conformance/{{YYYY-MM-DD}}-findings.json`,
37
+ written ALONGSIDE this report (not after it) — one JSON entry per `### n.` finding below,
38
+ `id` matching `{{n}}`. See reference/conform.md step 5 for the exact shape and the write-then-
39
+ `em conform-findings check`-it discipline. A later `em conform-supersede --locus --by` call
40
+ records the ruling on both the JSON (locus/resolvedBy/resolvedOn) and this report's banner
41
+ in one call. -->
35
42
 
36
43
  ### {{n}}. {{Short finding title}}
37
44
 
@@ -7,10 +7,13 @@ Keep it current at the end of every working session.
7
7
  # Event Modeling Progress — {{Model Name}}
8
8
 
9
9
  - **Model file:** `{{model-name}}.em`
10
+ - **Em version:** {{X.Y.Z}}
10
11
  - **Current phase:** {{discover | extract | model | slice | implement | conform | review | validate}}
11
12
  - **Current step:** {{1–7, see methodology; or extraction round R1–R7}}
12
13
  - **Last updated:** {{YYYY-MM-DD}}
13
14
  - **Last conformance:** {{YYYY-MM-DD @ <target-repo revision> — report: conformance/<date>-report.md | never}}
15
+ - **Model version:** {{N | none}}
16
+ - **Certified:** {{v<N> @ <target-repo revision> (YYYY-MM-DD) | never}}
14
17
  - **Last stakeholder review:** {{YYYY-MM-DD — attendees: see Participants | never}}
15
18
 
16
19
  ## Session inputs
package/README.md CHANGED
@@ -118,6 +118,7 @@ built this way.
118
118
  | [docs/cli.md](docs/cli.md) | Every command and flag |
119
119
  | [docs/validation.md](docs/validation.md) | Every rule `em validate` checks, and the fixes |
120
120
  | [docs/ci.md](docs/ci.md) | Copy-paste CI recipes: validate `.em` changes, run conformance on a schedule |
121
+ | [docs/upgrading.md](docs/upgrading.md) | Bring a model repo forward across em releases: `em upgrade`, release by release |
121
122
  | [docs/timeline.md](docs/timeline.md) | The Two Laws of the Timeline |
122
123
  | [docs/ai-workflow.md](docs/ai-workflow.md) | The Claude Code skill: install, phases, artifacts |
123
124
  | [docs/dependencies.md](docs/dependencies.md) | What's bundled vs. what needs a system install |
@@ -73,6 +73,15 @@ export interface SliceDocExport {
73
73
  * em-tracker-bridge reads to find the mirrored ticket — its name/shape here is a cross-tool
74
74
  * contract, not just an internal display field. */
75
75
  tracking: string | null;
76
+ /** MIL-214: this slice's `version:` at the time a conform sweep last certified it (frontmatter
77
+ * `conformedVersion:`), written only by `em slice conform` — null when never certified. */
78
+ conformedVersion: number | null;
79
+ /** MIL-214: the target repo's revision that certification sweep diffed against (frontmatter
80
+ * `conformedAt:`), written only by `em slice conform` — null when never certified. */
81
+ conformedAt: string | null;
82
+ /** MIL-214: the local date that certification was recorded (frontmatter `conformedOn:`),
83
+ * written only by `em slice conform` — null when never certified. */
84
+ conformedOn: string | null;
76
85
  }
77
86
  export interface SliceDocJoinResult {
78
87
  doc: SliceDocExport;