@milehimikey/em 1.11.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 (154) 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 +22 -6
  5. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  6. package/.claude/skills/event-modeling-implement/SKILL.md +12 -6
  7. package/.claude/skills/event-modeling-implement/reference/implement.md +165 -18
  8. package/.claude/skills/event-modeling-review/SKILL.md +5 -1
  9. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +57 -8
  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 +69 -0
  12. package/.claude/skills/event-modeling-shared/templates/conformance-report.md +8 -1
  13. package/.claude/skills/event-modeling-shared/templates/constitution.md +8 -1
  14. package/.claude/skills/event-modeling-shared/templates/state.md +3 -0
  15. package/README.md +1 -0
  16. package/dist/catalog/build.js +42 -1
  17. package/dist/catalog/build.js.map +1 -1
  18. package/dist/catalog/coverage.d.ts +15 -0
  19. package/dist/catalog/coverage.js +34 -0
  20. package/dist/catalog/coverage.js.map +1 -0
  21. package/dist/catalog/docJoin.d.ts +20 -2
  22. package/dist/catalog/docJoin.js +32 -4
  23. package/dist/catalog/docJoin.js.map +1 -1
  24. package/dist/catalog/docModelConsistencyValidate.js +1 -1
  25. package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
  26. package/dist/catalog/driftSignal.d.ts +11 -5
  27. package/dist/catalog/driftSignal.js +18 -4
  28. package/dist/catalog/driftSignal.js.map +1 -1
  29. package/dist/catalog/modelVersionValidate.d.ts +9 -0
  30. package/dist/catalog/modelVersionValidate.js +38 -0
  31. package/dist/catalog/modelVersionValidate.js.map +1 -0
  32. package/dist/catalog/noteBindingValidate.js +18 -0
  33. package/dist/catalog/noteBindingValidate.js.map +1 -1
  34. package/dist/catalog/orphanedSliceDocValidate.d.ts +8 -1
  35. package/dist/catalog/orphanedSliceDocValidate.js +33 -6
  36. package/dist/catalog/orphanedSliceDocValidate.js.map +1 -1
  37. package/dist/catalog/pages.d.ts +16 -1
  38. package/dist/catalog/pages.js +16 -4
  39. package/dist/catalog/pages.js.map +1 -1
  40. package/dist/catalog/sliceDoc.d.ts +12 -0
  41. package/dist/catalog/sliceDoc.js +15 -0
  42. package/dist/catalog/sliceDoc.js.map +1 -1
  43. package/dist/catalog/sliceReadyValidate.d.ts +12 -1
  44. package/dist/catalog/sliceReadyValidate.js +4 -4
  45. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  46. package/dist/cli/agentsMd.js +9 -3
  47. package/dist/cli/agentsMd.js.map +1 -1
  48. package/dist/cli/ciInit.js +20 -0
  49. package/dist/cli/ciInit.js.map +1 -1
  50. package/dist/cli/conformScope.d.ts +1 -0
  51. package/dist/cli/conformScope.js +13 -4
  52. package/dist/cli/conformScope.js.map +1 -1
  53. package/dist/cli/conformSupersede.d.ts +24 -2
  54. package/dist/cli/conformSupersede.js +44 -3
  55. package/dist/cli/conformSupersede.js.map +1 -1
  56. package/dist/cli/coverage.d.ts +34 -9
  57. package/dist/cli/coverage.js +61 -24
  58. package/dist/cli/coverage.js.map +1 -1
  59. package/dist/cli/findings.d.ts +134 -0
  60. package/dist/cli/findings.js +355 -0
  61. package/dist/cli/findings.js.map +1 -0
  62. package/dist/cli/markImplemented.js +12 -1
  63. package/dist/cli/markImplemented.js.map +1 -1
  64. package/dist/cli/metrics.d.ts +93 -0
  65. package/dist/cli/metrics.js +331 -0
  66. package/dist/cli/metrics.js.map +1 -0
  67. package/dist/cli/modelVersion.d.ts +139 -0
  68. package/dist/cli/modelVersion.js +256 -0
  69. package/dist/cli/modelVersion.js.map +1 -0
  70. package/dist/cli/ratify.js +17 -1
  71. package/dist/cli/ratify.js.map +1 -1
  72. package/dist/cli/reratify.d.ts +17 -0
  73. package/dist/cli/reratify.js +37 -2
  74. package/dist/cli/reratify.js.map +1 -1
  75. package/dist/cli/review.js +12 -1
  76. package/dist/cli/review.js.map +1 -1
  77. package/dist/cli/skillCheck.js +2 -2
  78. package/dist/cli/skillCheck.js.map +1 -1
  79. package/dist/cli/sliceConform.d.ts +50 -0
  80. package/dist/cli/sliceConform.js +206 -0
  81. package/dist/cli/sliceConform.js.map +1 -0
  82. package/dist/cli/sliceIndex.d.ts +2 -0
  83. package/dist/cli/sliceIndex.js +24 -7
  84. package/dist/cli/sliceIndex.js.map +1 -1
  85. package/dist/cli/sliceLink.js +16 -0
  86. package/dist/cli/sliceLink.js.map +1 -1
  87. package/dist/cli/sliceNew.d.ts +12 -0
  88. package/dist/cli/sliceNew.js +23 -0
  89. package/dist/cli/sliceNew.js.map +1 -1
  90. package/dist/cli/sliceStubAll.d.ts +76 -0
  91. package/dist/cli/sliceStubAll.js +189 -0
  92. package/dist/cli/sliceStubAll.js.map +1 -0
  93. package/dist/cli/stateFile.d.ts +63 -7
  94. package/dist/cli/stateFile.js +179 -18
  95. package/dist/cli/stateFile.js.map +1 -1
  96. package/dist/cli/status.d.ts +90 -1
  97. package/dist/cli/status.js +151 -16
  98. package/dist/cli/status.js.map +1 -1
  99. package/dist/cli/upgrade.d.ts +122 -0
  100. package/dist/cli/upgrade.js +476 -0
  101. package/dist/cli/upgrade.js.map +1 -0
  102. package/dist/cli.js +586 -66
  103. package/dist/cli.js.map +1 -1
  104. package/dist/emit/coverageJson.d.ts +2 -2
  105. package/dist/emit/coverageJson.js +6 -2
  106. package/dist/emit/coverageJson.js.map +1 -1
  107. package/dist/emit/freshnessJson.d.ts +1 -1
  108. package/dist/emit/freshnessJson.js +3 -1
  109. package/dist/emit/freshnessJson.js.map +1 -1
  110. package/dist/emit/json.d.ts +34 -1
  111. package/dist/emit/json.js +84 -2
  112. package/dist/emit/json.js.map +1 -1
  113. package/dist/emit/metricsJson.d.ts +7 -0
  114. package/dist/emit/metricsJson.js +27 -0
  115. package/dist/emit/metricsJson.js.map +1 -0
  116. package/dist/emit/statusJson.d.ts +1 -1
  117. package/dist/emit/statusJson.js +22 -1
  118. package/dist/emit/statusJson.js.map +1 -1
  119. package/dist/emit/upgradeJson.d.ts +5 -0
  120. package/dist/emit/upgradeJson.js +26 -0
  121. package/dist/emit/upgradeJson.js.map +1 -0
  122. package/dist/emit/validateJson.d.ts +10 -5
  123. package/dist/emit/validateJson.js +22 -8
  124. package/dist/emit/validateJson.js.map +1 -1
  125. package/dist/mcp/server.d.ts +1 -1
  126. package/dist/mcp/server.js +151 -22
  127. package/dist/mcp/server.js.map +1 -1
  128. package/dist/model/continuation.d.ts +23 -0
  129. package/dist/model/continuation.js +49 -0
  130. package/dist/model/continuation.js.map +1 -0
  131. package/dist/model/queryIndex.js +1 -1
  132. package/dist/model/queryIndex.js.map +1 -1
  133. package/dist/model/rules.d.ts +13 -0
  134. package/dist/model/rules.js +18 -0
  135. package/dist/model/rules.js.map +1 -1
  136. package/dist/model/sliceDocIndex.d.ts +12 -5
  137. package/dist/model/sliceDocIndex.js +18 -7
  138. package/dist/model/sliceDocIndex.js.map +1 -1
  139. package/dist/model/validate.d.ts +20 -0
  140. package/dist/model/validate.js +94 -45
  141. package/dist/model/validate.js.map +1 -1
  142. package/dist/render/sliceStatus.d.ts +3 -3
  143. package/dist/render/sliceStatus.js +27 -29
  144. package/dist/render/sliceStatus.js.map +1 -1
  145. package/dist/templates.d.ts +3 -3
  146. package/dist/templates.js +15 -2
  147. package/dist/templates.js.map +1 -1
  148. package/dist/util/localDate.d.ts +2 -0
  149. package/dist/util/localDate.js +17 -0
  150. package/dist/util/localDate.js.map +1 -0
  151. package/dist/util/semver.d.ts +19 -0
  152. package/dist/util/semver.js +54 -0
  153. package/dist/util/semver.js.map +1 -0
  154. package/package.json +1 -1
@@ -49,17 +49,27 @@ 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
55
61
  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
62
  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)
63
+ em slice review <file> <slice-key> --on <date> # review date, YYYY-MM-DD (default: today, local date)
58
64
  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
59
65
  em slice ratify <file> <slice-key> --by <name> # the ratifier's name
60
- em slice ratify <file> <slice-key> --on <date> # ratification date, YYYY-MM-DD (default: today)
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
- em conform-supersede <file> <report-path> --on <date> # ruling date, YYYY-MM-DD (default: today)
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, ...)
@@ -102,9 +123,10 @@ em ledger <file> --from <rev> # baseline revisio
102
123
  em ledger <file> --to <rev> # compare revision (default: current working tree)
103
124
  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
104
125
  em ledger <file> --json # print a JSON document instead of the text report (see docs/cli.md)
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
126
+ em coverage <file> # check that every INV-* invariant ID cited in an implemented slice doc is cited by a test under --tests <dir> (MIL-130/MIL-207) — mechanizes reference/implement.md's definition-of-done citation check; advisory by default, --strict for CI; --include-ready also counts ready-to-implement docs (forward-looking report); a continuation slice (an `again` view instance with no doc of its own, MIL-208) is excluded from the report — its invariants, if any, live in the originating slice's own doc
106
127
  em coverage <file> --tests <dir> # directory to scan recursively for test files citing invariant IDs
107
128
  em coverage <file> --strict # exit non-zero if any invariant ID has zero citations (CI)
129
+ em coverage <file> --include-ready # also count ready-to-implement docs, not just implemented (MIL-207)
108
130
  em coverage <file> --json # print a JSON document instead of the text report (see docs/cli.md)
109
131
  em status <files> # deterministic state-of-the-system rollup over one or more .em models: slices by lifecycle status, driftSignal breakdown, invariant coverage totals (with --tests), open issue markers + unchecked Open Questions, and last-conformance commits-behind-HEAD (MIL-163, see docs/cli.md)
110
132
  em status <files> --tests <dir> # directory to scan for INV-* test citations — enables invariant coverage totals
@@ -116,6 +138,10 @@ em status <files> -o, --out <path> # write output to
116
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)
117
139
  em freshness <file> --repo <path> # git repo to compute behind-HEAD in (default: the model's own directory)
118
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)
119
145
  em query consumers <files> # views/reactions consuming an event, plus their slices
120
146
  em query consumers <files> --event <ref-or-name> # the event's export ref or display name
121
147
  em query consumers <files> --json # print a JSON document instead of the text report
@@ -152,7 +178,7 @@ em query path <files> --json # print a JSON doc
152
178
  em system <manifest> # verify a seam manifest (system.yaml) against its models' exports — every `public` event/view bound to a reaction in another model, the cross-model half of "both ends of a flow" (MIL-194, see docs/cli.md)
153
179
  em system <manifest> --json # print a JSON document instead of the text report (see docs/cli.md)
154
180
  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
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
181
+ em mcp # start an MCP (Model Context Protocol) server over stdio (MIL-21) — a structured, agent-facing alternative to shelling out to `em`; every tool mirrors a CLI `--json` surface byte-for-byte. docs/mcp.md isn't vendored into the skill bundle — for the handshake and the full, current tool table (the list changes as commands gain MCP parity, so it's not repeated here — MIL-187) see https://github.com/milehimikey/em/blob/main/docs/mcp.md. Equivalent to running the `em-mcp` bin directly
156
182
  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
183
  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)
158
184
  em skill install --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
@@ -164,6 +190,10 @@ em ci init <model> # install .github/
164
190
  em ci init <model> --tests <dir> # test directory the coverage/status steps scan for INV-* citations
165
191
  em ci init <model> -f, --force # replace an existing workflow file that has no GENERATED markers
166
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)
167
197
  ```
168
198
  <!-- GENERATED:cli:end -->
169
199
 
@@ -224,7 +254,11 @@ type Name { field: Type, ... } # named structured type, reusable from any f
224
254
  connected to one another — continuity is implied by the shared name, and the events reaching each
225
255
  instance are what show the view changing. `again` with no earlier declaration is a validation error.
226
256
  Use `again` (not a plain repeated `view` name) whenever the view is referenced by a
227
- `from`/`arrow` — plain repeats are only warning-free while unreferenced.
257
+ `from`/`arrow` — plain repeats are only warning-free while unreferenced. A later instance is
258
+ also a **continuation** for the doc lifecycle (MIL-208): it stays a slice on the timeline but
259
+ is not a spec unit — no doc, no status, no ratification, no PR of its own; the originating
260
+ slice's doc carries its behavior and `em export`'s `alsoReads` carries the union of events it
261
+ reads. See `slice-doc-schema.md`'s "Continuations" section for the full contract.
228
262
  - **`loops-to "View"`** (events only): marks an event as re-feeding an earlier read model — a
229
263
  poll/retry loop where a later fact must re-trigger an automation that already ran (a lapsed
230
264
  reservation re-notifying the next person waiting in line, and similar re-triggers). Written
@@ -265,6 +299,14 @@ type Name { field: Type, ... } # named structured type, reusable from any f
265
299
  this model). `em export` carries it as `public: true`/`false`; `em diff` reports a flip as
266
300
  `event marked public`/`event unmarked public` (the entry cites the element's kind, so a
267
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).
268
310
  - **`tag`** (events only): declares a DCB (Dynamic Consistency Boundary) tag key. Three forms:
269
311
  a trailing `tag` on a field line inside the event's `{ … }` block (`priceId: UUID tag`, or
270
312
  typeless `priceId tag`) — an identity tag, key defaults to the field's own name; a bare field
@@ -516,6 +558,11 @@ slice "Read Quote — created" {
516
558
  Keep the read model adjacent to its event, and keep a sub-flow that detours into another context
517
559
  together rather than parking it at the end of the model.
518
560
 
561
+ What this means for whoever builds a slice, not just whoever draws it: a `ui` trigger or
562
+ consumer is an obligation to ship an endpoint, no `ui` means internal-only, and `public` marks
563
+ the published integration surface — see `reference/implement.md`'s "Interface obligations"
564
+ paragraph in the `event-modeling-implement` skill (§2).
565
+
519
566
  ### Reaction wiring — no positional gotcha
520
567
 
521
568
  A reaction's command is inferred from **same-slice presence**, exactly like `ui -> command`: put
@@ -646,6 +693,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
646
693
  | `both-ends-of-a-flow/ui-unbacked` | warning | `ui` with no read model or command | Add a `view` it displays, or the command it triggers. |
647
694
  | `both-ends-of-a-flow/view-unconsumed` | warning | Read model with no consumer | Add a `ui` or reaction that consumes it, or drop this instance. |
648
695
  | `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. |
696
+ | `continuation-has-own-doc` | warning | Continuation slice has its own doc | Fold this doc's scenarios into slices/<originating-key>.md and delete it; a later `view X again` instance is documented by the view's originating slice. |
649
697
  | `cross-model-slice-doc-collision` | warning | Colliding slice doc path across models | Give each model its own directory (see docs/cli.md, "Multi-model projects"). |
650
698
  | `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
699
  | `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`. |
@@ -670,6 +718,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
670
718
  | `lineage-version-impossible` | error | Impossible lineage version | Fix the referenced version, or ratify the target slice first. |
671
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. |
672
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. |
673
722
  | `note-binding-dangling` | warning | Dangling cross-slice note | Create the doc at that path, or fix/remove the note. |
674
723
  | `note-binding-extra` | warning | Extra doc-binding note, ignored | Remove the note, or point it at the slice's actual bound doc. |
675
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
@@ -180,6 +210,12 @@ reads a bound doc's canonical fields (`em export`'s `slice.doc`, `em validate --
180
210
  `implementedIn`/lineage/Open Questions, not the covered slice's own (nonexistent) file. A doc
181
211
  covers its own canonical slice key implicitly — `covers` never needs to list it.
182
212
 
213
+ `em catalog` (MIL-137) is one such consumer, via the same filename+frontmatter `covers:` scan
214
+ (`src/catalog/coverage.ts`), never the `note` clause itself: a covered slice's detail page shows
215
+ the covering doc's Status, but a "Documented as part of &lt;covering slice&gt;" banner instead
216
+ of the covering doc's body — linking to the covering slice's own catalog page rather than
217
+ inlining content authored under a different slice's name.
218
+
183
219
  `covers` is a plain list of slice keys (`<slice-key>`, e.g. `detect-unpaid-orders`), **not** the
184
220
  `<slice-key>@v<N>` ref grammar the lineage keys use above: coverage is a standing "this doc also
185
221
  serves that slice" declaration, with no version component — unlike lineage, which records a
@@ -195,6 +231,39 @@ the mismatch, as its own separate rule (MIL-126) — see
195
231
  that just doesn't ratify (`covers` doesn't list this slice back), and an extra note doing nothing
196
232
  in a slice that's already bound elsewhere, each get their own diagnostic there.
197
233
 
234
+ ## Continuations
235
+
236
+ Since `em` 1.12.0 (MIL-208): a `view X again` instance is a **continuation** of the slice
237
+ holding `X`'s first (originating) declaration — one read model, fed by more than one event over
238
+ the timeline — not a spec unit of its own. Unlike `covers` above, this is fully automatic and
239
+ structural: no frontmatter, no `note`, nothing to author. A slice is a continuation iff every
240
+ non-`ui` element it declares is a `view` with `again === true`; the doc join resolves it
241
+ straight through to the originating slice's own doc, exactly the way a ratified `covers:`
242
+ binding resolves a covered slice — same `doc.found`/`status`/`driftSignal`, plus a
243
+ `continuationOf: <originating-key>` field naming why. `em export`, `em status`, `em coverage`,
244
+ `em slice index`, `em catalog`, and the render pipeline's Slice Status legend all resolve a
245
+ continuation slice this way; `em slice ratify`/`review`/`reratify`/`mark-implemented` and
246
+ `em slice new --wire` refuse on one outright, naming the originating slice to act on instead —
247
+ a continuation slice has no status/ratification of its own to flip.
248
+
249
+ The originating slice's own `em export` object also gains `alsoReads: [{ event, atSlice }]` —
250
+ the union of events every later `again` instance of that view reads via `from`, in timeline
251
+ order — so implementing the read model from one export never means walking every `again`
252
+ slice's doc by hand to assemble the full event list.
253
+
254
+ **Authoring convention:** a continuation's behavior belongs in the originating doc's own
255
+ `## Scenarios (Given / When / Then)` section — one scenario per event a later `again` instance
256
+ feeds — rather than in a doc of its own; no new frontmatter is needed to record it.
257
+
258
+ **Legacy own doc wins.** A slice that would otherwise be a continuation but still carries its
259
+ own doc — a `note "slices/<again-key>.md"` binding it, or a stray `slices/<again-key>.md` file
260
+ left over from before this rule existed — keeps today's per-instance behavior unchanged: its own
261
+ doc, its own status, its own ratification. `em validate`'s `continuation-has-own-doc` warning
262
+ (see [validation.md](https://github.com/milehimikey/em/blob/main/docs/validation.md)) flags it either way, so an author can fold that doc's
263
+ scenarios into `slices/<originating-key>.md` and delete it. This is the migration path — an
264
+ existing model with per-`again`-instance docs keeps working exactly as before until each one is
265
+ folded in.
266
+
198
267
  ## Delta section: grammar and lifecycle
199
268
 
200
269
  `## Delta` is the body section that pairs with a re-ratification (see "`status` under
@@ -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
 
@@ -46,6 +46,9 @@ technical choice" an implementing agent would otherwise make alone.
46
46
  - **Architectural pattern:** {{e.g. event-sourced CQRS with vertical slices; layered service over
47
47
  a relational store; functional core / imperative shell}}
48
48
  - **Where a slice's code lives:** {{one package per slice, per context, ...}}
49
+ - **Interface conventions:** {{resource style — REST/RPC/GraphQL, etc.; error/rejection mapping;
50
+ versioning; auth at the edge — the shape every `ui`-triggered command or `ui`-consumed view's
51
+ endpoint follows, decided once here rather than invented per slice}}
49
52
 
50
53
  Routing — which implementation skill or approach each of the four slice patterns uses:
51
54
 
@@ -105,7 +108,11 @@ it is silent, these apply.
105
108
  - **Reviewers:** {{who — a team, a CODEOWNERS rule, the slice's ratifier}}
106
109
  - **What blocks a merge:** {{e.g. a failing suite, an uncovered invariant, a gap that belongs back
107
110
  at the model}}
108
- - **Branch / commit conventions:** {{...}}
111
+ - **Branch / commit conventions:** {{...}} — name only branch naming and commit-message style
112
+ here. **One slice doc = one PR is the implement contract's rule, not this document's to
113
+ amend** — a `covers:` doc ships as one PR for both keys; a read model is one doc, so a later
114
+ `again` instance is a continuation of the doc that first declared it, not a PR of its own. See
115
+ the agent guide's §5/§10.
109
116
  - **At merge:** run `em slice mark-implemented <model>.em <slice-key> <pr-url>` — the one edit an
110
117
  implementing agent makes to a ratified slice doc, run by {{the PR author | CI}}. Never bump
111
118
  `version:`; that moves only when a delta is ratified.
@@ -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 |
@@ -13,10 +13,12 @@
13
13
  import { existsSync, readFileSync } from "node:fs";
14
14
  import { copyFile, mkdir, writeFile } from "node:fs/promises";
15
15
  import { basename, dirname, extname, join } from "node:path";
16
+ import { continuationOf } from "../model/continuation.js";
16
17
  import { layoutDot, composeSvg, writeRendered } from "../render/render.js";
17
18
  import { buildSliceDiagram } from "../render/sliceDiagram.js";
18
19
  import { dedupe, kebabSlug } from "../util/slug.js";
19
20
  import { classifySlicePattern } from "./classify.js";
21
+ import { readCoverageDocs } from "./coverage.js";
20
22
  import { detectSliceDocCollisions } from "./modelCollisionValidate.js";
21
23
  import { parseSliceDoc } from "./sliceDoc.js";
22
24
  import { renderIndexPage, renderSlicePage } from "./pages.js";
@@ -51,6 +53,11 @@ export async function buildCatalog(inputs, opts) {
51
53
  if (refs.diagnostics.length > 0)
52
54
  diagnostics.push({ file, diagnostics: refs.diagnostics });
53
55
  const sliceSummaries = [];
56
+ // MIL-137: built lazily, only if some slice below actually lacks its own doc — same
57
+ // shared scan (src/catalog/coverage.ts) the render pipeline's Slice Status legend uses
58
+ // (src/render/sliceStatus.ts), so a covered slice never resolves to a different doc
59
+ // depending on which command asked.
60
+ let coverageDocs = null;
54
61
  for (let i = 0; i < model.slices.length; i++) {
55
62
  const slice = model.slices[i];
56
63
  const sliceKey = refs.sliceKeys[i];
@@ -63,7 +70,40 @@ export async function buildCatalog(inputs, opts) {
63
70
  // instead of silently rendering the first slice's doc content on both pages.
64
71
  const docRelPath = join("slices", `${sliceKey}.md`);
65
72
  const docPath = join(dirname(file), docRelPath);
66
- const doc = existsSync(docPath) ? parseSliceDoc(readFileSync(docPath, "utf8")) : null;
73
+ const ownDoc = existsSync(docPath) ? parseSliceDoc(readFileSync(docPath, "utf8")) : null;
74
+ // MIL-137: no own doc -> fall back to a sibling doc that ratifies coverage of this slice
75
+ // (MIL-121 `covers:`). The covering doc's status/pattern act as this slice's own for the
76
+ // index Status column and this page's header badge (same "one doc, one status" rule the
77
+ // render pipeline's Slice Status legend already applies), but its BODY is never inlined —
78
+ // the detail page shows a "documented as part of" banner linking to the covering slice's
79
+ // own page instead, never the covering doc's content rendered under a different slice's
80
+ // name.
81
+ let doc = ownDoc;
82
+ let coveredBy = null;
83
+ if (!ownDoc) {
84
+ coverageDocs ??= readCoverageDocs(join(dirname(file), "slices"));
85
+ const covering = coverageDocs.get(sliceKey);
86
+ if (covering) {
87
+ doc = covering.doc;
88
+ const coveringIndex = refs.sliceKeys.indexOf(covering.key);
89
+ coveredBy = { key: covering.key, name: coveringIndex >= 0 ? model.slices[coveringIndex].name : covering.key, kind: "covers" };
90
+ }
91
+ else {
92
+ // MIL-208: still no doc — check whether this slice is a CONTINUATION (an again-view-
93
+ // only slice) of another slice's own file, filename-convention resolution's own
94
+ // fallback (mirrors render/sliceStatus.ts's readSliceDocs). Checked last, after both
95
+ // the own-file and MIL-121 covers: lookups have already come up empty.
96
+ const continuation = continuationOf(model, refs, i);
97
+ if (continuation) {
98
+ const originatingPath = join(dirname(file), "slices", `${continuation.sliceKey}.md`);
99
+ if (existsSync(originatingPath)) {
100
+ doc = parseSliceDoc(readFileSync(originatingPath, "utf8"));
101
+ const originatingSlice = model.slices[continuation.sliceIndex];
102
+ coveredBy = { key: continuation.sliceKey, name: originatingSlice.name, kind: "continuation" };
103
+ }
104
+ }
105
+ }
106
+ }
67
107
  // Same sibling-file convention as the doc lookup above, one extension over: a
68
108
  // slice diagram authored by `em render --slice` (per the event-modeling skill)
69
109
  // is copied in as-is so the catalog stays self-contained; otherwise built
@@ -103,6 +143,7 @@ export async function buildCatalog(inputs, opts) {
103
143
  pattern,
104
144
  elementRefs,
105
145
  doc,
146
+ coveredBy,
106
147
  docExpectedPath: docRelPath,
107
148
  });
108
149
  await writeFile(join(slicesDir, `${sliceKey}.html`), page, "utf8");
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/catalog/build.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/catalog/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { SliceDoc } from "./sliceDoc.js";
2
+ /** A covering doc, plus the covering slice's OWN key (its `slices/<key>.md` filename stem,
3
+ * stripped of the extension). Callers that need to link to or name the covering slice itself
4
+ * (`em catalog`'s "documented as part of" banner) get it here rather than re-deriving "which
5
+ * slice does this doc actually belong to" from `doc`, which has no notion of its own key. */
6
+ export interface CoveringDoc {
7
+ doc: SliceDoc;
8
+ key: string;
9
+ }
10
+ /** Scans every `slices/*.md` file (sorted, for deterministic first-wins on an overlapping
11
+ * `covers:` claim) and maps each covered slice key to the doc — and that doc's own key — that
12
+ * covers it. Directory-scan, not the single-file existsSync check every other doc lookup in em
13
+ * uses — the one precedent for it is `em ledger`'s (src/cli/ledgerCheck.ts) own
14
+ * `readdirSync(slicesDir)` sweep. */
15
+ export declare function readCoverageDocs(slicesDir: string): Map<string, CoveringDoc>;
@@ -0,0 +1,34 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Shared MIL-121 "covered by" doc-discovery: scans `slices/*.md` for a frontmatter `covers:`
3
+ // entry naming a slice that has no doc of its own. Every consumer that needs this fallback —
4
+ // the render pipeline's Slice Status legend (src/render/sliceStatus.ts) and `em catalog`'s
5
+ // detail pages (src/catalog/build.ts, MIL-137) — shares this one scan, so a covered slice never
6
+ // resolves to a different doc (or a different notion of which slice covers it) depending on
7
+ // which command asked. Filename+frontmatter discovery only: never reads Element.note — that
8
+ // cross-note is docJoin.ts's concern (`em export`'s doc join, `em validate --slice-ready`), not
9
+ // this module's; test/catalog.e2e.test.ts pins "never reads note" as an invariant for `em
10
+ // catalog`, and this module is exactly as blind to it.
11
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ import { parseSliceDoc } from "./sliceDoc.js";
14
+ /** Scans every `slices/*.md` file (sorted, for deterministic first-wins on an overlapping
15
+ * `covers:` claim) and maps each covered slice key to the doc — and that doc's own key — that
16
+ * covers it. Directory-scan, not the single-file existsSync check every other doc lookup in em
17
+ * uses — the one precedent for it is `em ledger`'s (src/cli/ledgerCheck.ts) own
18
+ * `readdirSync(slicesDir)` sweep. */
19
+ export function readCoverageDocs(slicesDir) {
20
+ const coverage = new Map();
21
+ if (!existsSync(slicesDir))
22
+ return coverage;
23
+ const files = readdirSync(slicesDir).filter((f) => f.endsWith(".md")).sort();
24
+ for (const file of files) {
25
+ const doc = parseSliceDoc(readFileSync(join(slicesDir, file), "utf8"));
26
+ const key = file.slice(0, -".md".length);
27
+ for (const coveredKey of doc.covers) {
28
+ if (!coverage.has(coveredKey))
29
+ coverage.set(coveredKey, { doc, key });
30
+ }
31
+ }
32
+ return coverage;
33
+ }
34
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/catalog/coverage.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,6FAA6F;AAC7F,6FAA6F;AAC7F,2FAA2F;AAC3F,gGAAgG;AAChG,4FAA4F;AAC5F,4FAA4F;AAC5F,gGAAgG;AAChG,0FAA0F;AAC1F,uDAAuD;AAEvD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAY,MAAM,eAAe,CAAC;AAWxD;;;;sCAIsC;AACtC,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAChD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5C,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACvE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACzC,KAAK,MAAM,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -1,4 +1,5 @@
1
- import { Slice } from "../model/model.js";
1
+ import { NormalizedModel, Slice } from "../model/model.js";
2
+ import { RefsResult } from "../model/refs.js";
2
3
  import { Diagnostic } from "../model/validate.js";
3
4
  import { DriftSignalKind } from "./driftSignal.js";
4
5
  import { SliceDoc, SliceRef } from "./sliceDoc.js";
@@ -72,10 +73,27 @@ export interface SliceDocExport {
72
73
  * em-tracker-bridge reads to find the mirrored ticket — its name/shape here is a cross-tool
73
74
  * contract, not just an internal display field. */
74
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;
75
85
  }
76
86
  export interface SliceDocJoinResult {
77
87
  doc: SliceDocExport;
78
88
  diagnostics: Diagnostic[];
89
+ /** MIL-208: non-null when this slice is a CONTINUATION of another slice — an again-view-only
90
+ * slice with no legacy doc of its own (see the module comment's "Legacy own doc wins")
91
+ * — naming that originating slice's export key. `doc` is byte-identical to the originating
92
+ * slice's own join in that case (the SAME object, not a re-derived copy): `em export
93
+ * --slice <again-key>` reports the real status via `doc` while `continuationOf` says why.
94
+ * Null for every ordinary slice, and for a continuation slice that still carries its own
95
+ * legacy doc (a `continuation-has-own-doc` warning fires instead — see rules.ts). */
96
+ continuationOf: string | null;
79
97
  }
80
98
  /**
81
99
  * Resolve one slice's doc join. `baseDir` is the `.em` file's directory (doc paths, like note
@@ -84,4 +102,4 @@ export interface SliceDocJoinResult {
84
102
  * entries use) so the returned diagnostics point at the same identities every other export
85
103
  * diagnostic does — never a second identifier scheme.
86
104
  */
87
- export declare function resolveSliceDocJoin(slice: Slice, sliceKey: string, baseDir: string, elementRefOf: (id: string) => string): SliceDocJoinResult;
105
+ export declare function resolveSliceDocJoin(model: NormalizedModel, refs: RefsResult, slice: Slice, sliceKey: string, baseDir: string, elementRefOf: (id: string) => string): SliceDocJoinResult;