@milehimikey/em 1.8.1 → 1.9.1

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 +58 -404
  2. package/.claude/skills/event-modeling-conform/SKILL.md +77 -0
  3. package/.claude/skills/{event-modeling → event-modeling-conform}/reference/conform.md +40 -14
  4. package/.claude/skills/event-modeling-design/SKILL.md +139 -0
  5. package/.claude/skills/event-modeling-discover/SKILL.md +89 -0
  6. package/.claude/skills/{event-modeling → event-modeling-discover}/reference/extract.md +3 -2
  7. package/.claude/skills/event-modeling-implement/SKILL.md +40 -0
  8. package/.claude/skills/{event-modeling → event-modeling-implement}/reference/implement.md +3 -2
  9. package/.claude/skills/event-modeling-review/SKILL.md +58 -0
  10. package/.claude/skills/{event-modeling → event-modeling-shared}/reference/em-dsl.md +115 -83
  11. package/.claude/skills/{event-modeling → event-modeling-shared}/reference/methodology.md +6 -5
  12. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +150 -0
  13. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +381 -0
  14. package/.claude/skills/{event-modeling → event-modeling-shared}/templates/model-readme.md +2 -2
  15. package/.claude/skills/{event-modeling → event-modeling-shared}/templates/slice.md +57 -14
  16. package/README.md +24 -4
  17. package/dist/catalog/build.js +9 -0
  18. package/dist/catalog/build.js.map +1 -1
  19. package/dist/catalog/docJoin.d.ts +15 -0
  20. package/dist/catalog/docJoin.js +8 -0
  21. package/dist/catalog/docJoin.js.map +1 -1
  22. package/dist/catalog/modelCollisionValidate.d.ts +22 -0
  23. package/dist/catalog/modelCollisionValidate.js +67 -0
  24. package/dist/catalog/modelCollisionValidate.js.map +1 -0
  25. package/dist/catalog/orphanedSliceDocValidate.d.ts +11 -0
  26. package/dist/catalog/orphanedSliceDocValidate.js +98 -0
  27. package/dist/catalog/orphanedSliceDocValidate.js.map +1 -0
  28. package/dist/catalog/sliceDoc.d.ts +15 -0
  29. package/dist/catalog/sliceDoc.js +18 -0
  30. package/dist/catalog/sliceDoc.js.map +1 -1
  31. package/dist/cli/changelogBuild.d.ts +12 -0
  32. package/dist/cli/changelogBuild.js +89 -0
  33. package/dist/cli/changelogBuild.js.map +1 -0
  34. package/dist/cli/ciInit.d.ts +42 -0
  35. package/dist/cli/ciInit.js +298 -0
  36. package/dist/cli/ciInit.js.map +1 -0
  37. package/dist/cli/conformSupersede.d.ts +32 -0
  38. package/dist/cli/conformSupersede.js +102 -0
  39. package/dist/cli/conformSupersede.js.map +1 -0
  40. package/dist/cli/coverage.d.ts +6 -1
  41. package/dist/cli/coverage.js +28 -3
  42. package/dist/cli/coverage.js.map +1 -1
  43. package/dist/cli/frontmatterSurgery.d.ts +28 -0
  44. package/dist/cli/frontmatterSurgery.js +66 -0
  45. package/dist/cli/frontmatterSurgery.js.map +1 -0
  46. package/dist/cli/markImplemented.js +6 -45
  47. package/dist/cli/markImplemented.js.map +1 -1
  48. package/dist/cli/ratify.d.ts +41 -0
  49. package/dist/cli/ratify.js +151 -0
  50. package/dist/cli/ratify.js.map +1 -0
  51. package/dist/cli/reratify.d.ts +33 -0
  52. package/dist/cli/reratify.js +137 -0
  53. package/dist/cli/reratify.js.map +1 -0
  54. package/dist/cli/skillCheck.d.ts +6 -0
  55. package/dist/cli/skillCheck.js +46 -0
  56. package/dist/cli/skillCheck.js.map +1 -1
  57. package/dist/cli/skillDirs.d.ts +15 -0
  58. package/dist/cli/skillDirs.js +34 -0
  59. package/dist/cli/skillDirs.js.map +1 -0
  60. package/dist/cli/skillSync.d.ts +10 -0
  61. package/dist/cli/skillSync.js +15 -0
  62. package/dist/cli/skillSync.js.map +1 -1
  63. package/dist/cli/sliceIndex.d.ts +3 -0
  64. package/dist/cli/sliceIndex.js +6 -2
  65. package/dist/cli/sliceIndex.js.map +1 -1
  66. package/dist/cli/sliceLink.d.ts +52 -0
  67. package/dist/cli/sliceLink.js +152 -0
  68. package/dist/cli/sliceLink.js.map +1 -0
  69. package/dist/cli/status.d.ts +261 -0
  70. package/dist/cli/status.js +536 -0
  71. package/dist/cli/status.js.map +1 -0
  72. package/dist/cli/usageLog.d.ts +75 -0
  73. package/dist/cli/usageLog.js +193 -0
  74. package/dist/cli/usageLog.js.map +1 -0
  75. package/dist/cli.js +503 -132
  76. package/dist/cli.js.map +1 -1
  77. package/dist/emit/freshnessJson.d.ts +6 -0
  78. package/dist/emit/freshnessJson.js +24 -0
  79. package/dist/emit/freshnessJson.js.map +1 -0
  80. package/dist/emit/json.d.ts +62 -8
  81. package/dist/emit/json.js +14 -3
  82. package/dist/emit/json.js.map +1 -1
  83. package/dist/emit/statusJson.d.ts +7 -0
  84. package/dist/emit/statusJson.js +39 -0
  85. package/dist/emit/statusJson.js.map +1 -0
  86. package/dist/emit/typespec.d.ts +26 -0
  87. package/dist/emit/typespec.js +356 -0
  88. package/dist/emit/typespec.js.map +1 -0
  89. package/dist/emit/usageReportJson.d.ts +3 -0
  90. package/dist/emit/usageReportJson.js +23 -0
  91. package/dist/emit/usageReportJson.js.map +1 -0
  92. package/dist/mcp/server.d.ts +1 -1
  93. package/dist/mcp/server.js +301 -11
  94. package/dist/mcp/server.js.map +1 -1
  95. package/dist/model/rules.d.ts +13 -0
  96. package/dist/model/rules.js +20 -0
  97. package/dist/model/rules.js.map +1 -1
  98. package/dist/templates.d.ts +1 -1
  99. package/dist/templates.js +2 -2
  100. package/dist/util/markers.d.ts +16 -4
  101. package/dist/util/markers.js +23 -10
  102. package/dist/util/markers.js.map +1 -1
  103. package/package.json +2 -1
  104. /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/conformance-report.md +0 -0
  105. /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/state.md +0 -0
@@ -0,0 +1,381 @@
1
+ <!-- GENERATED FILE — do not hand-edit. Synced from docs/slice-doc-schema.md by
2
+ scripts/generate-skill-docs.ts (`npm run docs:generate`); `npm run docs:check` fails
3
+ on drift. A few links below are rewritten for this vendored location (see that
4
+ script's buildVendoredSliceDocSchema for exactly what changes and why) — everything
5
+ else is identical to the source doc. -->
6
+
7
+ # Slice-doc frontmatter schema
8
+
9
+ This documents the machine-read YAML frontmatter dialect `src/catalog/sliceDoc.ts` parses out
10
+ of `slices/<slice-key>.md` — the contract `em export`'s slice-frontmatter join reads. It covers
11
+ the **frontmatter block only**. Body/prose authoring conventions (Intent, Scenarios, Open
12
+ Questions, …) live in the event-modeling skill's
13
+ [`templates/slice.md`](../templates/slice.md), not here — with two
14
+ exceptions:
15
+
16
+ - The `## Open Questions` section's GFM checkboxes (`- [ ]` / `- [x]`) *are* now machine-parsed
17
+ by `sliceDoc.ts` too (`openQuestionsTotal`/`openQuestionsUnchecked`, MIL-87), feeding
18
+ `em validate --slice-ready <key>` (see [validation.md#slice-readiness](https://github.com/milehimikey/em/blob/main/docs/validation.md#slice-readiness)).
19
+ The section's cross-version lifecycle (MIL-156) is documented here too, alongside `## Delta`'s
20
+ — see [Open Questions section: lifecycle](#open-questions-section-lifecycle) below. Everything
21
+ else about the section's authoring — what a good open question looks like, when to resolve one
22
+ — stays owned by the skill template; this doc only covers the counting mechanics and lifecycle.
23
+ - The `## Delta` section's grammar and lifecycle (MIL-88) is documented here, in full, alongside
24
+ the lineage keys it complements — a 2026-08-14 ruling deliberately kept the slice-doc contract
25
+ as one document rather than splitting a structurally-related convention across two files, even
26
+ though (unlike Open Questions) `## Delta` is currently display-only prose that no `em` command
27
+ parses yet. See [Delta section: grammar and lifecycle](#delta-section-grammar-and-lifecycle)
28
+ below; authoring voice/tone for the section still lives in the skill template, same as every
29
+ other body section.
30
+
31
+ ## How the parser reads frontmatter
32
+
33
+ A leading `---` / `---` fence, at the literal start of the file (like Jekyll/Hugo — a guidance
34
+ comment above the fence means the frontmatter doesn't count yet). Inside the fence: top-level
35
+ scalar `key: value` lines only — **no lists, no nesting, no real YAML**. Quoted values have
36
+ their outer quotes stripped; a value that's empty after trimming is dropped. Keys are matched
37
+ case-sensitively as `[A-Za-z][\w-]*` but folded to lowercase for lookup. Any line that doesn't
38
+ match that shape — including `#`-prefixed comment lines, blank lines, and YAML list
39
+ continuation lines (` - item`) — is silently skipped. An unterminated fence (no closing `---`)
40
+ means "no frontmatter at all": the whole file is treated as body.
41
+
42
+ No `yaml` dependency, by design — a real YAML parser was added and reverted (commit `6de0a05`,
43
+ "keep the Two Laws of the Timeline"). This is a deliberately shallow line-scan, and stays that
44
+ way: real YAML would also start accepting nesting/lists that this schema explicitly doesn't
45
+ define a meaning for.
46
+
47
+ `SliceDoc` (`src/catalog/sliceDoc.ts`) also exposes `frontmatterPresent` (was a well-formed
48
+ fence found and closed at all) and `missingRequiredFields` (which of the required-at-every-
49
+ status keys, above, were absent) — the mechanical basis `em export`'s doc join (MIL-91) uses
50
+ to decide `frontmatter-invalid` without re-deriving frontmatter-shape rules of its own.
51
+
52
+ ## Canonical keys
53
+
54
+ | Key | Type | Grammar / enum | Read by |
55
+ |---|---|---|---|
56
+ | `schemaVersion` | integer | `1` (current dialect version) | its *value* is not read back by any `em` command today (reserved) — but its *presence* is required by `hasUsableFrontmatter()`: a doc omitting it is `frontmatter-invalid` (see below) |
57
+ | `pattern` | string | `state-change` \| `state-view` \| `automation` \| `translation` | authored/informational only — `em catalog` and `em export` both derive pattern from the `.em` AST instead (`em export`'s `slice.pattern`, schema `1.4`) |
58
+ | `swimlane` | string | free text, `<Persona> → <Context>` | display-only |
59
+ | `status` | string, case-insensitive | `draft` \| `reviewed` \| `ready-to-implement` \| `implemented` | `em catalog`, `em render`/`em watch` header coloring; joined into `em export`'s `slice.doc.status` (schema `1.4`); paired with `implementedIn` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) |
60
+ | `version` | integer | positive integer, starts at `1` | joined into `em export`'s `slice.doc.version` (schema `1.4`, MIL-91) |
61
+ | `implementedIn` | string | free text (PR/commit link) | joined into `em export`'s `slice.doc.implementedIn` (schema `1.4`, MIL-91); paired with `status` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) |
62
+ | `split-from` | single ref | `<slice-key>@v<N>` | joined into `em export`'s `slice.doc.splitFrom` (schema `1.4`, MIL-91) and `em diff`'s `slice-added` entries (schema `1.6`, MIL-84); current-tree referential checks by `em validate` (MIL-84 — see [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage)) |
63
+ | `merged-from` | list of refs | comma-separated `<slice-key>@v<N>, ...` | joined into `em export`'s `slice.doc.mergedFrom` (schema `1.4`, MIL-91) and `em diff`'s `slice-added` entries (schema `1.6`, MIL-84); current-tree referential checks by `em validate` (MIL-84 — see [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage)) |
64
+ | `superseded-by` | list of refs | comma-separated `<slice-key>@v<N>, ...` | joined into `em export`'s `slice.doc.supersededBy` (schema `1.4`, MIL-91) and `em diff`'s `slice-removed` entries (schema `1.6`, MIL-84); current-tree referential checks by `em validate` (MIL-84 — see [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage)) |
65
+ | `covers` | list of slice keys | comma-separated `<slice-key>, ...` (plain keys — **not** the `<slice-key>@v<N>` ref grammar above) | ratifies a cross-slice binding (MIL-121 — see [Cross-slice coverage](#cross-slice-coverage-covers) below): read by `em export`'s doc join, `em validate --slice-ready`, and (best-effort) `em render`/`em watch`'s Slice Status legend |
66
+ | `ratifiedBy` | string | free text (typically a person's name) | written only by `em slice ratify` (MIL-165 — see [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-slice-ratify-file-slice-key---by-name)); joined into `em export`'s `slice.doc.ratifiedBy` (schema `1.8`) and `em slice index`'s Ratified by column |
67
+ | `ratifiedOn` | string | `YYYY-MM-DD` | written only by `em slice ratify` (MIL-165); joined into `em export`'s `slice.doc.ratifiedOn` (schema `1.8`) |
68
+ | `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[]` |
69
+ | `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 |
70
+
71
+ Five keys — `schemaVersion`/`pattern`/`swimlane`/`status`/`version` — are what
72
+ `hasUsableFrontmatter()` requires: a doc omitting any of them is `frontmatter-invalid` to
73
+ `em export`'s doc join and to `em validate --slice-ready` (the parser itself never *throws*
74
+ over an omission — it reports). `implementedIn` and the lineage keys are the genuinely
75
+ optional ones; `implementedIn`'s required-once-implemented rule in the table below is
76
+ convention, not parser-enforced.
77
+
78
+ ## Required vs optional, by `status`
79
+
80
+ | Key | draft | reviewed | ready-to-implement | implemented |
81
+ |---|---|---|---|---|
82
+ | `schemaVersion`, `pattern`, `swimlane`, `status` | required | required | required | required |
83
+ | `version` | required (starts at `1`) | required | required | required |
84
+ | `implementedIn` | optional | optional | optional | required once the slice has *ever* reached `implemented` — may still name a **prior** version's PR during re-ratification, see below |
85
+ | `split-from`, `merged-from`, `superseded-by` | optional in every state — present only on docs created by a split/merge, or on a doc that has been retired |
86
+ | `covers` | optional in every state — present only on a doc that deliberately also serves another slice (MIL-121, see below); most docs never carry it |
87
+ | `ratifiedBy`, `ratifiedOn` | optional in every state — present once `em slice ratify` (MIL-165) has run at least once; a doc predating this feature, or ratified by hand, simply omits both |
88
+ | `owner`, `tracking` | optional in every state — hand-filled whenever a team wants a who-holds-this / external-tracker link; most docs never carry either (MIL-171) |
89
+
90
+ `em export`'s slice-doc join (MIL-91) is the first `em` command to mechanically check the
91
+ required row above: a bound doc missing any of `schemaVersion`/`pattern`/`swimlane`/`status`/
92
+ `version` (or missing a frontmatter block entirely) reports `slice.doc.reason:
93
+ "frontmatter-invalid"` plus a warning diagnostic — report, never fail, same as every other
94
+ export finding. `implementedIn`'s conditional requirement (once a slice has *ever* reached
95
+ `implemented`) is **not** checked — that needs git history no doc-only parse has access to.
96
+ No `em` command fails a build over this table. `em validate`'s frontmatter-coherence check
97
+ (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) warns
98
+ (never fails) on the one combination checkable without git history: `status: implemented` with
99
+ no `implementedIn` link at all.
100
+
101
+ ## Lineage: grammar and cardinality
102
+
103
+ Grammar: `<slice-key>@v<N>` — `<slice-key>` is the referenced slice's kebab-case filename stem
104
+ (matches `kebabSlug()`, `src/util/slug.ts`); `<N>` is a positive integer, the referenced
105
+ slice's `version` at the point of reference.
106
+
107
+ | Key | Cardinality | Why |
108
+ |---|---|---|
109
+ | `split-from` | **single** ref | A slice splits *from* exactly one parent, even though a split can produce multiple successors. |
110
+ | `merged-from` | **list**, comma-separated | A merge combines two or more source slices into one surviving doc. |
111
+ | `superseded-by` | **list**, comma-separated | The retired side of a split names potentially multiple successors; the retired side of a merge or a simple rename usually names exactly one, but the grammar stays list-shaped either way. |
112
+
113
+ **Worked example** (a split): `checkout` v4 splits into `checkout` v5 plus a new
114
+ `apply-discount` v1. The retired parent doc, `checkout.md`, gets:
115
+
116
+ ```yaml
117
+ version: 4
118
+ status: implemented
119
+ superseded-by: checkout@v5, apply-discount@v1
120
+ ```
121
+
122
+ Both surviving docs carry the back-link — `checkout.md` (now at v5) and `apply-discount.md`
123
+ each get:
124
+
125
+ ```yaml
126
+ split-from: checkout@v4
127
+ ```
128
+
129
+ **Scope note:** the parser (`sliceDoc.ts`) itself still never checks that a referenced
130
+ slice/version actually *exists* — that stays deliberately out of scope here, by design (fs-free,
131
+ pure). A malformed or dangling ref still parses (its `raw` text is preserved, `sliceKey`/
132
+ `version` come back `null` for a malformed value); referential checking is a separate, fs-aware
133
+ layer.
134
+
135
+ `em validate` (`src/catalog/lineageValidate.ts`, MIL-84) is that layer. It checks what the
136
+ *current tree* can prove wrong and stays silent about what it can't — see
137
+ [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage) for exactly what's checked (grammar,
138
+ self-reference/cycles, dangling `superseded-by`, impossible version arithmetic) versus what's
139
+ deliberately never flagged (a `split-from`/`merged-from` naming a key legitimately absent from
140
+ the current tree — the normal state after a real split/merge). Deep historical verification —
141
+ did `slice@vN` really, once, exist? — stays out of core validate; the reason that's sufficient
142
+ is a same-commit authoring convention, not new plumbing (`em ledger`, below, is the opt-in,
143
+ two-revision command for the related-but-different version/content invariant):
144
+
145
+ > Lineage refs (`split-from:`/`merged-from:`/`superseded-by:`) are written in the same
146
+ > ratification commit that performs the operation they record, so the PR diff contains both
147
+ > sides of the claim (the predecessor at its final version, and the ref naming it). The review
148
+ > airlock is the history check.
149
+
150
+ ## Cross-slice coverage: `covers`
151
+
152
+ Since `em` 1.7.1 (MIL-120), the canonical Automation/Translation shape is two raw slices: a
153
+ bare `view` slice, and the reaction+command+event slice that reads it. Each slice can still bind
154
+ its own `slices/<key>.md` doc the ordinary way, but a team documenting the whole design unit as
155
+ **one** doc needs a way to make the view-only slice count as doc-bound too, without splitting
156
+ the write-up back into two files. `covers` (MIL-121) is that escape hatch — a deliberately
157
+ two-ended handshake, not a one-sided claim:
158
+
159
+ 1. In the `.em`, an element in the *covered* slice (the one with no doc of its own) carries
160
+ `note "slices/<other-key>.md"` — a path in the ordinary `slices/<key>.md` shape, just naming
161
+ a *different* slice's canonical doc instead of its own.
162
+ 2. That other doc's own frontmatter ratifies the coverage with `covers`, naming the covered
163
+ slice's key back — same comma-separated, single scalar-line shape as `merged-from`/
164
+ `superseded-by` (this parser's frontmatter dialect has **no** real YAML list support, see
165
+ above):
166
+ ```yaml
167
+ covers: detect-unpaid-orders
168
+ ```
169
+ Multiple covered slices: `covers: detect-unpaid-orders, some-other-slice`.
170
+
171
+ Both sides are required. A `note` naming another slice's doc with no matching `covers` entry on
172
+ that doc — the file is missing, its frontmatter isn't usable, or `covers` doesn't list this
173
+ slice back — leaves the noting slice exactly as unbound as if it had no note at all
174
+ (`no-doc-bound`); em never guesses at a one-sided claim. When ratified, every consumer that
175
+ reads a bound doc's canonical fields (`em export`'s `slice.doc`, `em validate --slice-ready`,
176
+ `em render`/`em watch`'s Slice Status legend) reads the **covering** doc's `status`/`version`/
177
+ `implementedIn`/lineage/Open Questions, not the covered slice's own (nonexistent) file. A doc
178
+ covers its own canonical slice key implicitly — `covers` never needs to list it.
179
+
180
+ `covers` is a plain list of slice keys (`<slice-key>`, e.g. `detect-unpaid-orders`), **not** the
181
+ `<slice-key>@v<N>` ref grammar the lineage keys use above: coverage is a standing "this doc also
182
+ serves that slice" declaration, with no version component — unlike lineage, which records a
183
+ specific historical hop.
184
+
185
+ **Out of scope for THIS join:** `resolveSliceDocJoin` itself never warns on a *mismatched*
186
+ cross-note (a note naming a doc that doesn't ratify it back) — the slice's doc join still stays
187
+ silently `no-doc-bound`, the same as never having written the note; the join's own job is
188
+ resolving the *winning* binding, not auditing every note that didn't win. `em validate` does flag
189
+ the mismatch, as its own separate rule (MIL-126) — see
190
+ [validation.md#note-binding-mismatch](https://github.com/milehimikey/em/blob/main/docs/validation.md#note-binding-mismatch): a dangling cross-note
191
+ (missing file), one pointing at a doc with unusable frontmatter, one pointing at a usable doc
192
+ that just doesn't ratify (`covers` doesn't list this slice back), and an extra note doing nothing
193
+ in a slice that's already bound elsewhere, each get their own diagnostic there.
194
+
195
+ ## Delta section: grammar and lifecycle
196
+
197
+ `## Delta` is the body section that pairs with a re-ratification (see "`status` under
198
+ re-ratification" below) — a structured record of what changed to produce the version currently
199
+ in the frontmatter, in typed operation blocks (MIL-88) rather than a one-line summary. Like the
200
+ lineage keys above, it's currently authored, not parsed — no `em` command reads *this section*
201
+ — but it's documented here rather than left purely to the skill template because its shape is a
202
+ structural contract other tooling may parse later, same reasoning as the lineage grammar.
203
+ `em diff --json` now emits the same four-word vocabulary on its own structural entries (`op:
204
+ "Added" | "Modified" | "Removed" | "Renamed"`, schema `1.7`, MIL-131 — see
205
+ [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-diff-old-new)), so the two sides speak the same language; comparing an
206
+ authored `## Delta` against the structural diff of the revisions it claims to describe is still
207
+ a separate, not-yet-built decision.
208
+
209
+ **The heading is always the literal string `## Delta` — never a variable heading** (e.g. never
210
+ `## Delta: v1 → v2`). Several consumers key on exact `##` text when locating sections in a slice
211
+ doc (the SDD-bridge's section splitter, the slice-to-spec mapping table, the redirect prompt
212
+ preambles); a heading that changes every ratification would break exact-match lookup and move
213
+ the anchor on every hop — the same problem a made-up per-doc key would cause anywhere else in
214
+ this schema. Version range, ratification date, and a one-line summary go on the **first line
215
+ inside** the section instead, as display text — not a frontmatter key, not parsed:
216
+
217
+ ```markdown
218
+ ## Delta
219
+ **v1 → v2, ratified 2026-08-12** — one-line summary of the ratified change.
220
+
221
+ ### Added
222
+ #### Requirement: {{title}} ({{stable ID, e.g. an INV-<MNEMONIC>-n from ## Invariants}})
223
+ {{requirement text}}
224
+ ##### Scenario: {{name}}
225
+ - **Given:** {{...}}
226
+ - **When:** {{...}}
227
+ - **Then:** {{...}}
228
+
229
+ ### Modified
230
+ (same shape as Added — the ID names the requirement that changed)
231
+
232
+ ### Removed
233
+ #### Requirement: {{title}} ({{stable ID}})
234
+ {{why it was removed}}
235
+
236
+ ### Renamed
237
+ - {{old title}} ({{old ID}}) → {{new title}} ({{new ID}})
238
+ ```
239
+
240
+ Stable IDs follow `INV-<MNEMONIC>-<n>` — a short (2-4 uppercase letters/digits) abbreviation of
241
+ the slice's key, unique among the model's slices, then a small integer, optionally letter-suffixed
242
+ for a closely-related sub-invariant (`INV-CHK-3a`) — see the Invariants section guidance in
243
+ [`templates/slice.md`](../templates/slice.md). Per-slice prefixes
244
+ make IDs globally unique across the model, so an `em coverage` citation is exact instead of
245
+ ambiguous across slices.
246
+
247
+ **Four `###` subsections, in that fixed order, Title Case singular words** — `Added`, `Modified`,
248
+ `Removed`, `Renamed` — not OpenSpec's shouting-case `ADDED Requirements` grouping. This is the
249
+ one deliberate divergence from the OpenSpec grammar the convention was validated against: the
250
+ four *operations* are retained (independent convergent design — OpenSpec arrived at the same
251
+ vocabulary from its own change-management-first angle), but OpenSpec's flat, file-per-delta
252
+ heading topology is not, because a slice doc's delta is a section inside a doc with its own
253
+ parsed heading contract, not a standalone file. Omit any of the four subsections with no entries
254
+ this hop — most re-ratifications touch one or two operations, not all four.
255
+
256
+ **`Renamed` is a requirement-level operation, not a slice-level one.** It records an invariant or
257
+ requirement *within this slice* being renamed or renumbered (e.g. `INV-CHK-3` → `INV-CHK-3a`) —
258
+ it is not a substitute for the `split-from`/`merged-from`/`superseded-by` lineage keys above,
259
+ which record the slice-doc-level operation of one doc splitting, merging, or being renamed into
260
+ another. `em diff`'s own `ChangeType` deliberately treats a slice/element rename as remove+add,
261
+ never a first-class rename (`src/model/diff.ts`: "a rename IS a model change") — `Renamed` here
262
+ is a finer grain, inside one otherwise-unchanged slice, and doesn't revisit that decision.
263
+
264
+ **Lifecycle: replace, never accumulate — the section is a window onto the latest hop, not a
265
+ scroll of every hop.** A slice's first version (`v1`) carries no `## Delta` section at all —
266
+ absence means no hop has happened yet, not an empty section. Ratifying `v2` writes the section
267
+ for the first time, in the same commit that bumps `version` and flips `status`. Ratifying `v3`
268
+ **overwrites** it with the `v2 → v3` hop; the `v1 → v2` content is not retained in the doc.
269
+ Full history is git's job — the same-commit ratification convention above makes `git log -p
270
+ slices/<name>.md` a precise, zero-maintenance delta archive — and a rolled-up multi-hop view, if
271
+ ever wanted, is a *rendering* (e.g. a future `em changelog`), never hand-maintained doc content.
272
+ An accumulating in-doc log would be a second history that diverges from git, grows unboundedly
273
+ over a slice's life, references requirements that have since moved (a *live* doc quietly going
274
+ stale reads as current, unlike git which is honestly historical), and forces every reader to
275
+ work out which delta is the live one — exactly the ambiguity `driftSignal` (MIL-85, below) is
276
+ built to avoid.
277
+
278
+ ## Open Questions section: lifecycle
279
+
280
+ `## Open Questions` (MIL-87 for the counting mechanics; MIL-156 for this lifecycle) had no
281
+ cross-version story until now: `countOpenQuestions()` counts every GFM task item under the
282
+ heading, but nothing ever pruned a resolved (`- [x]`) one — left unaddressed, the section grows
283
+ by accretion across every re-ratification of a long-lived slice, the same problem `## Delta`
284
+ (above) solves for by replacing its content wholesale each hop.
285
+
286
+ **Decision: prune resolved items on the same commit that ratifies the next version — replace,
287
+ never accumulate, same stance as `## Delta`, applied by hand instead of by section-rewrite.**
288
+ Concretely:
289
+
290
+ - While the *current* version is still being drafted or reviewed, a checked item can stay in the
291
+ doc — it's a visible record of what this round already decided, not yet an accumulation
292
+ problem (a slice's first version, `v1`, commonly ships with every question already resolved;
293
+ see `examples/order-fulfillment/slices/browse-catalog.md`).
294
+ - On the commit that bumps `version` and rewrites `## Delta` for the next hop, prune every
295
+ already-checked item from `## Open Questions` — keep only what's still open (`- [ ]`), plus
296
+ anything new that hop's change surfaced.
297
+ - Full history isn't lost: it lives in git the same way `## Delta`'s superseded hops do —
298
+ `git log -p slices/<name>.md` shows exactly which questions were open, and how they were
299
+ answered, at each prior version.
300
+
301
+ **Why not keep resolved items as a permanent audit trail instead?** Same reasoning `## Delta`'s
302
+ lifecycle section gives: an ever-growing scroll of resolved questions is a second history that
303
+ diverges from git, grows unboundedly over a long-lived slice's life, and forces every reader to
304
+ work out which resolutions are still relevant to the *current* version versus leftover from a
305
+ prior one — exactly the ambiguity a live, current-version-only section avoids.
306
+
307
+ **Not a parsing/grammar change.** `countOpenQuestions()` (`sliceDoc.ts`) and
308
+ `slice-ready-open-questions-unchecked` (`sliceReadyValidate.ts`,
309
+ [validation.md#slice-readiness](https://github.com/milehimikey/em/blob/main/docs/validation.md#slice-readiness)) are unaffected: both already
310
+ only care about the CURRENT doc's checkbox state, never about how many hops of history the
311
+ section has accumulated. This is purely an authoring convention — same "documented, not
312
+ mechanically enforced" status `## Delta`'s own lifecycle has (no `em` command checks that pruning
313
+ actually happened on re-ratification).
314
+
315
+ ## `status` under re-ratification
316
+
317
+ When a new version is ratified for a slice whose previous version already shipped, `status`
318
+ tracks the **current version's** implementation state, not a running "has this ever shipped"
319
+ flag. Ratifying v2 on an `implemented` slice flips `status` back to `ready-to-implement`, while
320
+ `implementedIn` keeps naming the v1 PR until v2 ships. That deliberate mismatch — version 2,
321
+ implemented-link still pointing at v1's work — is not staleness, it's the **drift signal**: a
322
+ reader, `em export`'s `slice.doc.driftSignal` (`"unpropagated-delta"`, schema `1.5`, MIL-85), and
323
+ the event-modeling skill's `conform` phase all read it the same way — a ratified delta hasn't
324
+ shipped yet, not a fresh finding against the still-live v1 code. `em validate`'s
325
+ frontmatter-coherence check (MIL-85) deliberately never flags this combination — only
326
+ `status: implemented` with no `implementedIn` link at all is checkable incoherence; see
327
+ [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence).
328
+
329
+ Pair a re-ratification with a `## Delta` section (see
330
+ [Delta section: grammar and lifecycle](#delta-section-grammar-and-lifecycle) above) recording
331
+ the ratified change in typed operation blocks, so it's reviewable without opening git.
332
+
333
+ **Mechanically checking the invariant this section describes:** `em ledger` (MIL-89, opt-in —
334
+ see [cli.md#em-ledger-file](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-ledger-file)) checks that `version:` and a slice doc's
335
+ content (body + lineage refs) always change together across two git revisions — a version bump
336
+ with no real content change, or a content change with no version bump, is a ledger bug.
337
+ It **deliberately excludes `status`/`implementedIn`** from what counts as "content" for exactly
338
+ the reason this section explains: a re-ratification legitimately flips `status` and leaves
339
+ `implementedIn` naming prior work, with no version bump of its own — including either field in
340
+ the comparison would flag every ordinary lifecycle transition as a false positive.
341
+
342
+ ## Unknown keys
343
+
344
+ **Captured, never read, never a warning or error.** Real docs in the wild carry extra
345
+ non-canonical keys — `id`, `title`, `model`, `created`, `updated`, and YAML-list-valued keys
346
+ like `upstreamEvents`. Every top-level `key: value` scalar line is captured internally
347
+ regardless of whether it's one of the keys above; only the keys this document defines are ever
348
+ read back out onto `SliceDoc`. A list-valued key's own line (`upstreamEvents:`, value empty) is
349
+ captured with an empty value; its `- item` continuation lines don't match the `key: value` shape
350
+ at all and are simply skipped. Nothing throws, nothing warns — adding a new non-canonical key to
351
+ a doc is always safe.
352
+
353
+ ## `schemaVersion` vs `version` — don't conflate these
354
+
355
+ | | `schemaVersion` | `version` |
356
+ |---|---|---|
357
+ | What it versions | The frontmatter dialect itself | This one slice's ratified definition |
358
+ | Who bumps it | `em` maintainers, when the dialect's canonical keys change | Whoever ratifies a delta on this slice |
359
+ | Cardinality | One value, same across every doc using this dialect | One per slice doc, incrementing per ratified delta |
360
+ | Current value | `1` (unchanged since MIL-86) | Starts at `1` |
361
+ | Read by `em` today | Value: no — captured, not exposed on `SliceDoc`. Presence: required for usable frontmatter | Yes — parsed onto `SliceDoc.version` |
362
+
363
+ ## Legacy status bullet line
364
+
365
+ Docs written before the frontmatter dialect existed may use `- **Status:** ...` instead of
366
+ frontmatter `status:` — still accepted, status only. `version` and the lineage keys have no
367
+ legacy form; they're frontmatter-only from the day they were introduced.
368
+
369
+ ## See also
370
+
371
+ - [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-render-file) — slice status colors
372
+ - [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-catalog-files) — pattern / doc lookup
373
+ - [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-slice-ratify-file-slice-key---by-name) — `em slice ratify`, the mechanized ratification act (`ratifiedBy`/`ratifiedOn`, schema `1.8`, MIL-165)
374
+ - [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-slice-reratify-file-slice-key) — `em slice reratify`, the mechanized re-ratification version bump/status flip (MIL-161)
375
+ - [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-export-file) — the `em export` join (`slice.pattern`/`slice.doc`, schema `1.4`, MIL-91), including `owner`/`tracking` (schema `1.9`, MIL-171)
376
+ - [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-diff-old-new) — the `em diff` lineage annotation (schema `1.6`, MIL-84)
377
+ - [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage) — `em validate`'s lineage-ref resolution (MIL-84)
378
+ - [validation.md#slice-readiness](https://github.com/milehimikey/em/blob/main/docs/validation.md#slice-readiness) — `em validate --slice-ready`'s note-binding gate, including the `covers` cross-binding (MIL-121)
379
+ - [cli.md#em-coverage-file---tests-dir](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-coverage-file---tests-dir) — `em coverage`'s INV-ID extraction, scoped to the Invariants/Delta sections' structural (non-nested) bullet lines (MIL-149/MIL-155/MIL-156)
380
+ - [validation.md#note-binding-mismatch](https://github.com/milehimikey/em/blob/main/docs/validation.md#note-binding-mismatch) — `em validate`'s check for a doc-shaped note that doesn't participate in its slice's binding (MIL-126)
381
+ - [`templates/slice.md`](../templates/slice.md) — the authored template
@@ -37,8 +37,8 @@ never connected to one another.
37
37
  `em slice index {{model-name}}.em` to (re)write the table below from the model and its
38
38
  slice docs; never hand-edit between the markers. -->
39
39
  <!-- GENERATED:slices:start -->
40
- | # | Slice | Pattern | Status | Implemented in | Design doc |
41
- |---|-------|---------|--------|----------------|------------|
40
+ | # | Slice | Pattern | Status | Ratified by | Owner | Tracking | Implemented in | Design doc |
41
+ |---|-------|---------|--------|-------------|-------|----------|----------------|------------|
42
42
  <!-- GENERATED:slices:end -->
43
43
 
44
44
  ## Status
@@ -22,7 +22,7 @@ ships; that mismatch is an intended drift signal, not a bug), and fill in the `#
22
22
  below with this hop's change, replacing whatever it held before (it always shows only the latest
23
23
  hop — full history lives in git, same-commit-ratification convention). Full grammar — the fixed
24
24
  heading, the four operation subsections, why the heading never varies, the replace-not-accumulate
25
- lifecycle — is documented in docs/slice-doc-schema.md#delta-section-grammar-and-lifecycle; treat
25
+ lifecycle — is documented in ../reference/slice-doc-schema.md#delta-section-grammar-and-lifecycle; treat
26
26
  it the same Socratic way as every other section here, don't generate it mechanically.
27
27
 
28
28
  The three lineage keys only apply when this doc was produced by a split, merge, or rename —
@@ -35,8 +35,19 @@ to write up. Delete it otherwise; most docs never carry it. Requires a matching
35
35
  `note "slices/{{this-slice-name}}.md"` on an element in the OTHER slice — this key alone doesn't
36
36
  bind anything. Plain slice keys, comma-separated — not the `<slice-key>@v<N>` ref grammar above.
37
37
 
38
+ `ratifiedBy`/`ratifiedOn` record who ratified this doc's current `status`/`version`, and when —
39
+ don't hand-fill these; run `em slice ratify <model>.em {{this-slice-name}} --by "<name>"` at the
40
+ handoff gate instead (docs/process.md#what-ratified-means), which flips `status` to
41
+ `ready-to-implement` and writes both in one edit.
42
+
43
+ `owner`/`tracking` are optional and, unlike `ratifiedBy`/`ratifiedOn`, hand-filled — no `em`
44
+ command writes either. `owner` is free text naming who (a person or team) holds this slice;
45
+ `tracking` is a URL into an external tracker (Jira, Linear, ...) mirroring it — the exact field
46
+ `em-tracker-bridge` reads to find the mirrored ticket. `em` only stores and displays both; it
47
+ never talks to a tracker itself. Delete both otherwise; most docs never carry them.
48
+
38
49
  Full machine schema — required-vs-optional keys per `status`, value types/enums, the
39
- unknown-key policy — is documented in docs/slice-doc-schema.md.
50
+ unknown-key policy — is documented in ../reference/slice-doc-schema.md.
40
51
 
41
52
  The diagram below is generated, not hand-drawn: `em render <model>.em --slice "{{Slice Name}}"
42
53
  -o slices/{{slice-name}}.svg` (kebab-case the slice name to match this doc's own filename).
@@ -51,14 +62,17 @@ version: 1
51
62
  implementedIn: {{PR/commit link — fill in once status is `implemented`}}
52
63
  # Lineage — only when this doc exists because of a split, merge, or rename (delete these three
53
64
  # lines otherwise). Grammar: <slice-key>@v<N>. See
54
- # docs/slice-doc-schema.md#lineage-grammar-and-cardinality.
65
+ # ../reference/slice-doc-schema.md#lineage-grammar-and-cardinality.
55
66
  # split-from: <slice-key>@v<N>
56
67
  # merged-from: <slice-key>@v<N>, <slice-key>@v<N>
57
68
  # superseded-by: <slice-key>@v<N>, <slice-key>@v<N>
58
69
  # Cross-slice coverage — only when this doc also covers another slice with no doc of its own
59
70
  # (e.g. the view-only half of a two-slice Automation/Translation, MIL-121); delete otherwise.
60
- # Plain slice keys, comma-separated. See docs/slice-doc-schema.md#cross-slice-coverage-covers.
71
+ # Plain slice keys, comma-separated. See ../reference/slice-doc-schema.md#cross-slice-coverage-covers.
61
72
  # covers: <slice-key>, <slice-key>
73
+ # Ownership / tracking — optional, hand-filled (no `em` command writes either); delete otherwise.
74
+ # owner: <person or team>
75
+ # tracking: <ticket URL>
62
76
  ---
63
77
  # Slice: {{Slice Name}}
64
78
 
@@ -67,7 +81,7 @@ implementedIn: {{PR/commit link — fill in once status is `implemented`}}
67
81
  <!-- Only on re-ratification of an already-`implemented` slice — omit this whole section on a
68
82
  slice's first version (v1 has no delta yet). The heading is always the literal `## Delta`,
69
83
  never `## Delta: vX → vY` — hop metadata is a display line inside the section instead (see
70
- docs/slice-doc-schema.md#delta-section-grammar-and-lifecycle for why). Replace this
84
+ ../reference/slice-doc-schema.md#delta-section-grammar-and-lifecycle for why). Replace this
71
85
  section's content wholesale on the next re-ratification — it shows only the latest hop, not
72
86
  an accumulating log. Omit any of the four subsections below with no entries this hop; keep
73
87
  the remaining ones in this order (Added/Modified/Removed/Renamed). `Renamed` is for a
@@ -81,7 +95,9 @@ implementedIn: {{PR/commit link — fill in once status is `implemented`}}
81
95
  #### Requirement: {{title}} ({{stable ID, e.g. an INV-{{MNEMONIC}}-n from Invariants below}})
82
96
  {{requirement text — same voice as an Invariants entry}}
83
97
  ##### Scenario: {{name}}
84
- - **GIVEN** {{...}} **WHEN** {{...}} **THEN** {{...}}
98
+ - **Given:** {{...}}
99
+ - **When:** {{...}}
100
+ - **Then:** {{...}}
85
101
 
86
102
  ### Modified
87
103
  <!-- Same shape as Added — the stable ID names the requirement that changed. -->
@@ -145,17 +161,34 @@ an event directly.}}
145
161
  <!-- What must ALWAYS hold. Give each a stable ID so tests and code can reference it:
146
162
  `INV-<MNEMONIC>-<n>`, where `<MNEMONIC>` is a short (2-4 letter/digit), slice-unique
147
163
  abbreviation of this slice's key (e.g. slice `checkout` -> `INV-CHK-1`) — see
148
- docs/slice-doc-schema.md. Add a letter suffix for a closely-related sub-invariant
149
- (`INV-CHK-3a`). -->
164
+ ../reference/slice-doc-schema.md. Add a letter suffix for a closely-related sub-invariant
165
+ (`INV-CHK-3a`). Keep the rule statement itself to one line. If it needs more — why the rule
166
+ exists, what "violation" looks like in practice, an edge case worth calling out — add that as
167
+ a nested bullet under the rule instead of one long run-on sentence: this section renders as
168
+ HTML (`em catalog`/`em render`/`em watch`), and a nested bullet stays visually distinct where
169
+ a wrapped sentence collapses into a wall of text. The ID must stay on the rule's own
170
+ top-level bullet line — `em coverage` extracts INV IDs from that line only, never from a
171
+ nested elaboration bullet underneath it. -->
150
172
  - **INV-{{MNEMONIC}}-1:** {{rule that the command enforces; violation ⇒ rejection}}
173
+ - {{optional: rationale, edge-case detail, or what "violation" means here — its own bullet}}
151
174
  - **INV-{{MNEMONIC}}-2:** {{...}}
152
175
 
153
176
  ## Scenarios (Given / When / Then)
154
- <!-- The executable specification. Cover the happy path AND the key rule boundaries. -->
155
- - **Happy path** — Given {{starting state / prior events}}, When {{command/trigger}},
156
- Then {{event(s) recorded}} and {{resulting read-model change}}.
157
- - **Rejected (INV-{{MNEMONIC}}-1)** — Given {{state}}, When {{command}}, Then {{rejected with reason}}; no event.
158
- - **{{Edge case}}** — Given {{...}}, When {{...}}, Then {{...}}.
177
+ <!-- The executable specification. Cover the happy path AND the key rule boundaries. Each
178
+ scenario is a case-label bullet with Given/When/Then as nested sub-bullets — not one run-on
179
+ sentence — so the case reads as distinct beats once rendered instead of a dense paragraph. -->
180
+ - **Happy path**
181
+ - **Given:** {{starting state / prior events}}
182
+ - **When:** {{command/trigger}}
183
+ - **Then:** {{event(s) recorded}} and {{resulting read-model change}}.
184
+ - **Rejected (INV-{{MNEMONIC}}-1)**
185
+ - **Given:** {{state}}
186
+ - **When:** {{command}}
187
+ - **Then:** rejected with reason; no event.
188
+ - **{{Edge case}}**
189
+ - **Given:** {{...}}
190
+ - **When:** {{...}}
191
+ - **Then:** {{...}}
159
192
 
160
193
  ## Alternate & Error Flows
161
194
  <!-- Failure paths, retries, compensations, timeouts, idempotency. -->
@@ -174,5 +207,15 @@ an event directly.}}
174
207
  - **Downstream read models / slices affected:** {{...}}
175
208
 
176
209
  ## Open Questions
177
- <!-- Park unresolved items here instead of guessing. Mirror them into .event-modeling.md. -->
210
+ <!-- Park unresolved items here instead of guessing. Mirror them into .event-modeling.md.
211
+
212
+ Lifecycle across versions: while THIS version is still being worked, a resolved item
213
+ (`- [x]`) can stay checked here as a visible record of what's already been decided this
214
+ round. On the same commit that ratifies the NEXT version (bumps `version`, rewrites
215
+ `## Delta` above) — prune every already-checked item from this list; keep only what's still
216
+ open, plus anything new the delta itself surfaced. Same "replace, never accumulate" stance
217
+ as `## Delta`, for the same reason: an ever-growing scroll of resolved questions goes stale
218
+ exactly like an accumulating Delta log would, and full history already lives in git
219
+ (`git log -p slices/<name>.md`) — this section is a live worklist, not an audit trail. See
220
+ ../reference/slice-doc-schema.md#open-questions-section-lifecycle. -->
178
221
  - [ ] {{question}}
package/README.md CHANGED
@@ -64,16 +64,35 @@ minutes, and [docs/workflow.md](docs/workflow.md) picks up where it leaves off:
64
64
  gets specified, gated in CI, handed to implementation, and checked against the code that
65
65
  implements it.
66
66
 
67
+ ## How it works
68
+
69
+ Model, gate, ratify, implement, check — repeat as the system evolves. Humans make the calls
70
+ that matter (build the model, ratify a slice, rule on drift); `em` and its agents handle the
71
+ mechanical parts in between. This is the loop in miniature — see
72
+ [docs/workflow.md](docs/workflow.md) for the full seven-stage lifecycle and
73
+ [docs/process.md](docs/process.md) for exactly who (or what) does each part.
74
+
75
+ ```mermaid
76
+ flowchart LR
77
+ Model["Model<br/>.em file"] --> Gate["Gate<br/>em validate in CI"]
78
+ Gate --> Ratify["Ratify a slice<br/>a human signs off"]
79
+ Ratify --> Implement["Implement<br/>agent or engineer builds it"]
80
+ Implement --> Conform["Conform<br/>em conform checks the code"]
81
+ Conform -->|drift found, ratified| Model
82
+ ```
83
+
67
84
  ## Model with AI
68
85
 
69
- `em` ships a Claude Code skill that runs a facilitated Event Modeling session: the AI asks
70
- the questions, you supply the domain, and the model renders live as it grows.
86
+ `em` ships a Claude Code skill bundle — one router skill plus five focused, SDLC-stage skills
87
+ (discover/extract, model/slice, implement, conform/validate, watch/review) — that runs a
88
+ facilitated Event Modeling session: the AI asks the questions, you supply the domain, and the
89
+ model renders live as it grows.
71
90
 
72
91
  ```bash
73
- em skill install # copy the skill into .claude/skills/event-modeling/
92
+ em skill install # copy the skill bundle into .claude/skills/
74
93
  ```
75
94
 
76
- Then run `/event-modeling` in Claude Code. The same skill also runs the reverse direction:
95
+ Then run `/event-modeling` in Claude Code. The same bundle also runs the reverse direction:
77
96
  `extract` derives a model from a system that already exists, and `conform` checks a model
78
97
  against the code implementing it and reports where they've drifted. See
79
98
  [docs/ai-workflow.md](docs/ai-workflow.md) for the phases and what a session produces, and the
@@ -98,6 +117,7 @@ built this way.
98
117
  | [docs/usage-data.md](docs/usage-data.md) | What usage data em captures, and how to roll it up for a retro |
99
118
  | [docs/architecture.md](docs/architecture.md) | How rendering works; why Graphviz, not PlantUML |
100
119
  | [docs/roadmap.md](docs/roadmap.md) | What's planned |
120
+ | [docs/decisions/](docs/decisions/) | Write-ups for open design questions (starts with [MIL-162](docs/decisions/mil-162-teachable-navigator.md), the stakeholder-portal decision) |
101
121
 
102
122
  ## Development
103
123
 
@@ -17,6 +17,7 @@ import { layoutDot, composeSvg, writeRendered } from "../render/render.js";
17
17
  import { buildSliceDiagram } from "../render/sliceDiagram.js";
18
18
  import { dedupe, kebabSlug } from "../util/slug.js";
19
19
  import { classifySlicePattern } from "./classify.js";
20
+ import { detectSliceDocCollisions } from "./modelCollisionValidate.js";
20
21
  import { parseSliceDoc } from "./sliceDoc.js";
21
22
  import { renderIndexPage, renderSlicePage } from "./pages.js";
22
23
  export async function buildCatalog(inputs, opts) {
@@ -28,6 +29,14 @@ export async function buildCatalog(inputs, opts) {
28
29
  const modelSummaries = [];
29
30
  const diagnostics = [];
30
31
  let sliceCount = 0;
32
+ // MIL-160: catalog's own doc discovery below (existsSync+parseSliceDoc, deliberately not
33
+ // routed through readSliceDoc.ts's `note`-aware convention — see this module's header
34
+ // comment) reads straight off `dirname(file)/slices/<key>.md`, same as every other doc-aware
35
+ // command — so two input models sharing a directory with overlapping slice keys would
36
+ // silently mix up whose doc is whose here too. Checked once, up front, over every input.
37
+ const collisions = detectSliceDocCollisions(inputs.map(({ file, refs }) => ({ file, sliceKeys: refs.sliceKeys })));
38
+ for (const d of collisions)
39
+ diagnostics.push({ file: d.refs?.[2] ?? "", diagnostics: [d] });
31
40
  for (const { file, model, grid, dot, refs } of inputs) {
32
41
  const modelKey = dedupe(kebabSlug(basename(file, extname(file))), usedModelKeys, "~");
33
42
  const modelDir = join(outDir, modelKey);
@@ -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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"}