@milehimikey/em 1.6.1 → 1.7.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 (92) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +60 -12
  2. package/.claude/skills/event-modeling/reference/conform.md +30 -8
  3. package/.claude/skills/event-modeling/reference/em-dsl.md +139 -23
  4. package/.claude/skills/event-modeling/reference/extract.md +2 -1
  5. package/.claude/skills/event-modeling/reference/implement.md +172 -0
  6. package/.claude/skills/event-modeling/templates/conformance-report.md +15 -9
  7. package/.claude/skills/event-modeling/templates/model-readme.md +3 -3
  8. package/.claude/skills/event-modeling/templates/slice.md +70 -4
  9. package/.claude/skills/event-modeling/templates/state.md +6 -5
  10. package/README.md +1 -0
  11. package/dist/catalog/build.d.ts +5 -0
  12. package/dist/catalog/build.js +1 -3
  13. package/dist/catalog/build.js.map +1 -1
  14. package/dist/catalog/docJoin.d.ts +39 -0
  15. package/dist/catalog/docJoin.js +86 -0
  16. package/dist/catalog/docJoin.js.map +1 -0
  17. package/dist/catalog/driftSignal.d.ts +16 -0
  18. package/dist/catalog/driftSignal.js +24 -0
  19. package/dist/catalog/driftSignal.js.map +1 -0
  20. package/dist/catalog/frontmatterCoherenceValidate.d.ts +8 -0
  21. package/dist/catalog/frontmatterCoherenceValidate.js +52 -0
  22. package/dist/catalog/frontmatterCoherenceValidate.js.map +1 -0
  23. package/dist/catalog/lineageValidate.d.ts +9 -0
  24. package/dist/catalog/lineageValidate.js +161 -0
  25. package/dist/catalog/lineageValidate.js.map +1 -0
  26. package/dist/catalog/readSliceDoc.d.ts +2 -0
  27. package/dist/catalog/readSliceDoc.js +21 -0
  28. package/dist/catalog/readSliceDoc.js.map +1 -0
  29. package/dist/catalog/sliceDoc.d.ts +94 -3
  30. package/dist/catalog/sliceDoc.js +157 -7
  31. package/dist/catalog/sliceDoc.js.map +1 -1
  32. package/dist/catalog/sliceReadyValidate.d.ts +11 -0
  33. package/dist/catalog/sliceReadyValidate.js +85 -0
  34. package/dist/catalog/sliceReadyValidate.js.map +1 -0
  35. package/dist/cli/diff-inputs.d.ts +20 -0
  36. package/dist/cli/diff-inputs.js +75 -18
  37. package/dist/cli/diff-inputs.js.map +1 -1
  38. package/dist/cli/ledgerCheck.d.ts +38 -0
  39. package/dist/cli/ledgerCheck.js +116 -0
  40. package/dist/cli/ledgerCheck.js.map +1 -0
  41. package/dist/cli/skillCheck.d.ts +18 -0
  42. package/dist/cli/skillCheck.js +63 -0
  43. package/dist/cli/skillCheck.js.map +1 -0
  44. package/dist/cli/skillSync.d.ts +22 -0
  45. package/dist/cli/skillSync.js +69 -0
  46. package/dist/cli/skillSync.js.map +1 -0
  47. package/dist/cli/skillVersionCheck.d.ts +32 -0
  48. package/dist/cli/skillVersionCheck.js +145 -0
  49. package/dist/cli/skillVersionCheck.js.map +1 -0
  50. package/dist/cli.d.ts +3 -1
  51. package/dist/cli.js +199 -32
  52. package/dist/cli.js.map +1 -1
  53. package/dist/emit/diffJson.d.ts +1 -1
  54. package/dist/emit/diffJson.js +18 -14
  55. package/dist/emit/diffJson.js.map +1 -1
  56. package/dist/emit/json.d.ts +10 -25
  57. package/dist/emit/json.js +70 -103
  58. package/dist/emit/json.js.map +1 -1
  59. package/dist/emit/ledgerJson.d.ts +5 -0
  60. package/dist/emit/ledgerJson.js +25 -0
  61. package/dist/emit/ledgerJson.js.map +1 -0
  62. package/dist/emit/skillCheckJson.d.ts +5 -0
  63. package/dist/emit/skillCheckJson.js +32 -0
  64. package/dist/emit/skillCheckJson.js.map +1 -0
  65. package/dist/model/diff.d.ts +44 -1
  66. package/dist/model/diff.js +51 -9
  67. package/dist/model/diff.js.map +1 -1
  68. package/dist/model/model.d.ts +1 -1
  69. package/dist/model/refs.d.ts +23 -0
  70. package/dist/model/refs.js +75 -0
  71. package/dist/model/refs.js.map +1 -0
  72. package/dist/model/rules.d.ts +252 -0
  73. package/dist/model/rules.js +253 -0
  74. package/dist/model/rules.js.map +1 -0
  75. package/dist/model/validate.d.ts +23 -1
  76. package/dist/model/validate.js +168 -47
  77. package/dist/model/validate.js.map +1 -1
  78. package/dist/parser/ast.d.ts +3 -2
  79. package/dist/parser/parser.js +23 -11
  80. package/dist/parser/parser.js.map +1 -1
  81. package/dist/pipeline.d.ts +9 -0
  82. package/dist/pipeline.js +4 -2
  83. package/dist/pipeline.js.map +1 -1
  84. package/dist/render/sliceStatus.js +1 -1
  85. package/dist/render/sliceStatus.js.map +1 -1
  86. package/dist/util/isMainModule.d.ts +14 -0
  87. package/dist/util/isMainModule.js +33 -0
  88. package/dist/util/isMainModule.js.map +1 -0
  89. package/dist/util/walkDir.d.ts +4 -0
  90. package/dist/util/walkDir.js +28 -0
  91. package/dist/util/walkDir.js.map +1 -0
  92. package/package.json +5 -2
@@ -1,28 +1,32 @@
1
1
  ---
2
2
  name: event-modeling
3
+ em-version: 1.7.0
3
4
  description: >-
4
5
  Guide a user and their team through building rigorous Event Models with the `em` CLI, using
5
6
  the 7 steps of Event Modeling and the 4 patterns (State Change, State View, Automation,
6
- Translation), and produce implementation-ready slice design documents. Use when the user wants
7
+ Translation), and produce implementation-ready slice docs. Use when the user wants
7
8
  to event-model a business process or system, do event modeling / event storming, design slices,
8
9
  build or edit an `.em` model, run the em tool, extract / reverse-engineer a current-state
9
10
  event model from an existing system or codebase (event-driven or legacy/procedural), or check
10
11
  a ratified model for drift against the codebase that implements it. Works in resumable phases
11
12
  via an argument: `discover` (steps 1-4, greenfield), `extract` (current-state model of an
12
- existing system), `model` (steps 5-7), `slice` (deep slice specs), `conform` (drift check
13
- against the codebase), plus `watch` and `validate`. With no argument it resumes from the saved
14
- state file.
13
+ existing system), `model` (steps 5-7), `slice` (deep slice specs), `implement` (build one
14
+ ratified slice into code, following the bundled agent guide), `conform` (drift check
15
+ against the codebase), plus `watch`, `review` (stakeholder walkthrough), and `validate`.
16
+ With no argument it resumes from the saved state file.
15
17
  ---
16
18
 
17
19
  # Event Modeling with `em`
18
20
 
19
21
  You are facilitating an Event Modeling session. Your job is to **extract an accurate model from
20
22
  the user through Socratic questioning** — never to invent the domain. You drive the `em` CLI to
21
- render the model live, and you produce implementation-ready slice design docs.
23
+ render the model live, and you produce implementation-ready slice docs.
22
24
 
23
25
  Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl.md` (DSL syntax and
24
26
  validation rules) before doing real work — they are the source of truth. Templates live in
25
- `templates/`.
27
+ `templates/`. References to `docs/*.md` throughout this skill mean the
28
+ [em repository's docs](https://github.com/milehimikey/em/tree/main/docs) — they are not
29
+ vendored alongside this skill.
26
30
 
27
31
  ## Operating principles (every phase)
28
32
 
@@ -79,8 +83,8 @@ validation rules) before doing real work — they are the source of truth. Templ
79
83
  - **Validate continuously.** Run `em validate` and fix errors/warnings as you go (see DSL ref).
80
84
  - **Save state at the end of every session** so work resumes cleanly. Also append one line to
81
85
  the Usage log: the phase(s) touched and the `em validate` diagnostic *categories* that fired
82
- (rule name only, e.g. "read model nothing consumes" — never the full message or domain
83
- content). This is the team's only usage signal today (`docs/usage-data.md`) — keep it cheap
86
+ (the exact fixed strings from `docs/usage-data.md`, e.g. "read model has no consumer" —
87
+ never the full message or domain content). This is the team's only usage signal today (`docs/usage-data.md`) — keep it cheap
84
88
  and habitual, not a task to skip.
85
89
 
86
90
  ## Preconditions (run first)
@@ -226,6 +230,16 @@ phases is the starting point, not the finished event set. **Every new event need
226
230
  real (someone has to see that the request was declined).
227
231
 
228
232
  For each slice:
233
+ 0. **Check for re-ratification first.** If `slices/<slice-name>.md` already exists with
234
+ `status: implemented`, this is a re-ratification, not fresh authoring: hold a Socratic
235
+ deep-dive on what changed (same rigor as step 1, scoped to the delta), update whichever
236
+ sections actually changed, then express the change as the `## Delta` section — fixed heading,
237
+ `### Added`/`Modified`/`Removed`/`Renamed` Requirement blocks each carrying its own scenarios
238
+ (see `templates/slice.md` and `docs/slice-doc-schema.md#delta-section-grammar-and-lifecycle`)
239
+ — **overwriting** whatever the section held before, never appending to it. Then continue at
240
+ step 2 to bump `version` and flip `status` back to `ready-to-implement`, leaving
241
+ `implementedIn` naming the prior version's PR (intended drift signal, not a bug — see the
242
+ schema doc). Otherwise, this is first-time authoring: continue at step 1.
229
243
  1. Hold a Socratic deep-dive to fill every section of `templates/slice.md`: intent, trigger/actor,
230
244
  command + field table (types & rules), event(s) + payload (mark immutable facts), invariants
231
245
  (give each a stable ID), Given/When/Then scenarios (happy path + rule boundaries + edge cases),
@@ -235,19 +249,51 @@ For each slice:
235
249
  adjacent real sources (OpenAPI specs, DB migrations, existing DTOs/event classes in sibling
236
250
  contexts) before finalizing field names/types or invariants — don't guess a shape that's
237
251
  already defined elsewhere.
238
- 2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name). Record the originating
252
+ 2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name), with the YAML
253
+ frontmatter block at the very top — all five of `schemaVersion`/`pattern`/`swimlane`/
254
+ `status`/`version` (kebab-case `pattern` value, `schemaVersion: 1`, `version` starting at
255
+ `1`; omitting any of the five makes the doc `frontmatter-invalid` to `em export` and the
256
+ readiness gate), plus `implementedIn` once shipped — the canonical, machine-read
257
+ metadata dialect (`- **Status:** ...` bullet lines are legacy/accepted input only; never
258
+ write new docs that way). When this doc exists because of a split, merge, or rename, add the
259
+ matching lineage key(s) too (`split-from`/`merged-from`/`superseded-by`, `<slice-key>@v<N>`
260
+ grammar — see `docs/slice-doc-schema.md` for the full schema). On a re-ratification (step 0),
261
+ bump `version` and flip `status` here instead of setting them fresh. Record the originating
239
262
  need (ticket/conversation link) in the Intent section when one exists.
240
263
  3. Render the slice's own diagram: `em render <model>.em --slice "<slice name>" -o
241
264
  slices/<slice-name>.svg` (kebab-case, matching the doc's filename) — redraws just this slice
242
265
  in its own canonical pattern shape, and add `![Diagram](./<slice-name>.svg)` near the top of
243
- the doc, right after the metadata bullet list.
266
+ the doc, right after the `# Slice:` heading (below the frontmatter block).
244
267
  4. Wire it into the `.em`: add `note "slices/<slice-name>.md"` to the slice's primary element
245
268
  (the command for State Change, the view for State View, the processor for Automation, the
246
269
  translation for Translation).
247
- 5. Update `README.md`'s slice index — the one canonical slice table (`draft` →
248
- `ready-to-implement`, later `implemented` once shipped, with `Implemented in:` filled in).
270
+ 5. Update `README.md`'s slice index — the one canonical slice table (`draft` → `reviewed` →
271
+ `ready-to-implement`, later `implemented` once shipped, with its Implemented-in link
272
+ filled in).
249
273
  6. Re-render and `em validate`.
250
274
 
275
+ When a slice is **ratified** — a human flips its `status` to `ready-to-implement` with every
276
+ open question resolved — it's ready to hand to an implementer. Confirm with
277
+ `em validate <model>.em --slice-ready <key>`, and point whoever (or whatever) implements it
278
+ at `reference/implement.md`.
279
+
280
+ ## Phase: `implement` — build one ratified slice
281
+
282
+ Goal: turn a single `ready-to-implement` slice into merged, verified code — the one phase
283
+ where the agent builds rather than facilitates.
284
+
285
+ **Read `reference/implement.md` before doing any implement work and follow it as the
286
+ contract.** In short: gate on `em validate <model>.em --slice-ready <key>` (stop and hand
287
+ back if it fails — never edit the doc to make the gate pass); treat the slice doc as the
288
+ spec (read-only, except the merge-time `status`/`implementedIn` flip); surface every gap to
289
+ the user instead of deciding it silently; cover every INV-n and every scenario with tests;
290
+ in spec-kit projects, allocate via em-sdd-bridge (redirect mode) and never run
291
+ `/speckit.specify`.
292
+
293
+ End of phase: PR merged, slice doc flipped to `status: implemented` with `implementedIn`
294
+ filled, `README.md`'s slice index updated. Implement doesn't chain to another phase — run it
295
+ once per ratified slice; `conform` later checks the result against the model.
296
+
251
297
  ## Phase: `conform` — drift check against the codebase
252
298
 
253
299
  Goal: check a **ratified** model (and its slice docs) against the codebase that implements it,
@@ -346,6 +392,7 @@ two slices and route it through a command.
346
392
 
347
393
  ## em command reference (quick)
348
394
 
395
+ <!-- GENERATED:cli-quick:start -- run `npm run docs:generate` to refresh, do not hand-edit -->
349
396
  ```bash
350
397
  em --version
351
398
  em init <name>.em # optional starter scaffold
@@ -357,6 +404,7 @@ em watch <name>.em -o <name>.svg # re-render on save (run in backgroun
357
404
  em watch <name>.em -o <name>.svg --serve # + live viewer with instant push-reload (--port N)
358
405
  # click "Review mode" for a slice-by-slice storyboard walkthrough
359
406
  ```
407
+ <!-- GENERATED:cli-quick:end -->
360
408
 
361
409
  Always finish a working session by: re-rendering, running `em validate`, and updating
362
410
  `.event-modeling.md` with the current phase/step, decisions, open questions, and a Usage log
@@ -34,10 +34,11 @@ difference is what you do with what you find: extract *builds* a model from the
34
34
 
35
35
  1. A canonical `.em` model with a state file (`.event-modeling.md`), reachable the same way the
36
36
  SKILL.md preconditions locate it.
37
- 2. The model should be ratified for the slices you're about to check — `slice` docs at
38
- `implemented` (or the status the user says corresponds to shipped code) for whichever slices
39
- are in scope. Checking a `draft` or `ready-to-implement` slice against code is a wasted walk;
40
- tell the user and skip it rather than reporting "drift" against a doc nobody built to yet.
37
+ 2. The slices you're about to check should be **implemented** — docs at `status: implemented`
38
+ (or the status the user says corresponds to shipped code) for whichever slices are in scope.
39
+ Checking a `draft` or `ready-to-implement` slice against code is a wasted walk — ratified or
40
+ not, nobody has built to it yet; tell the user and skip it rather than reporting "drift"
41
+ against it.
41
42
  3. The codebase to check: the state file's **Existing system refs** (populated during
42
43
  `extract`, if this model came from one) or ask the user for the repo path if not set. Record
43
44
  it if missing. In a headless/scheduled run with no user to ask, default to the repository
@@ -56,7 +57,7 @@ named invariants, Given/When/Then scenarios). Conform checks all three surfaces
56
57
  |---|---|---|---|
57
58
  | **Structural** — `.em` ↔ code | slices/elements/fields/flow | `em` (deterministic) | `em diff --json` on canonical vs. an as-is scratch model |
58
59
  | **Spec** — slice doc ↔ code | field rules not enforced, named invariants (`INV-*`) without enforcement/test sites, GWT scenarios without test sites | you (judgment) | per-slice evidence walk: each checkable claim in the doc mapped to a code/test site by name (`INV-CHK-4` → its enforcement site + its test) |
59
- | **Internal** — slice doc ↔ `.em` | the doc's header metadata (Pattern, Status) or its Command/Event/Read Model sections and field tables disagreeing with the model's own elements/fields | you (judgment, v1) | cross-check the doc's structured sections against the slice's model elements |
60
+ | **Internal** — slice doc ↔ `.em` | the doc's frontmatter (`pattern:`, `status:`) or its Command/Event/Read Model sections and field tables disagreeing with the model's own elements/fields | you (judgment, v1) | cross-check the doc's structured sections against the slice's model elements |
60
61
 
61
62
  The internal surface is deterministically checkable in principle (structured doc sections +
62
63
  `{ fields }` blocks) — promoting it to an `em validate` rule is a natural follow-up; file it as
@@ -74,7 +75,7 @@ authoritative.
74
75
  Default: **diff-scoped.** Read the state file's `Last conformance:` marker (date + target-repo
75
76
  revision). If it's set, run `git diff --name-only <recorded-revision>..HEAD` from the target
76
77
  repo's root to list the changed paths (`git log` alone prints commit subjects, not paths), then
77
- map those paths to slices (via each slice doc's known code locations, `Implemented in:` links,
78
+ map those paths to slices (via each slice doc's known code locations, `implementedIn:` links,
78
79
  or a quick Grep if those are stale) — that's your in-scope set. If the marker reads `never`
79
80
  (first run) or the user passes `--full`, scope is every `implemented` slice.
80
81
 
@@ -112,6 +113,18 @@ For each slice in scope:
112
113
  3). If it does, this is not a fresh finding to litigate: classify it as **Accepted
113
114
  divergence** (step 4) and cite the annotation text as the evidence. This is the specific,
114
115
  per-element, ratified counterpart to the idiom-level bullet above.
116
+ - **Unpropagated deltas (MIL-85):** before treating any slice-level finding as drift, check
117
+ that slice's `doc.driftSignal` in `em export --json` (see `docs/slice-doc-schema.md`'s
118
+ "`status` under re-ratification" section for the mechanics). `"unpropagated-delta"` means
119
+ the slice was re-ratified (a new
120
+ `version`, `status` flipped back off `implemented`) but `implementedIn` still names the
121
+ *prior* version's PR — the code you're evidence-walking is that prior version, on purpose.
122
+ Do not diff it against the newer doc content and do not report a mismatch as Real drift or
123
+ a Model gap; classify it as **Unpropagated delta** (step 4) instead and stop — the finding
124
+ is "this delta hasn't shipped yet," not a defect in the shipped code. `"implemented-
125
+ without-link"` (status: implemented, no link at all) is a genuine coherence problem, but
126
+ it's already surfaced by `em validate`'s `frontmatter-coherence-implemented-without-link`
127
+ warning — don't re-raise it here as a separate conform finding.
115
128
  3. Write what you found into the scratch model (`<model-name>-asis.em`, see below). **Seed the
116
129
  scratch model as a copy of the canonical `.em` and replace only the in-scope slices** with
117
130
  the as-is picture, leaving out-of-scope slices byte-identical. This is not an optimization:
@@ -178,6 +191,12 @@ step 2 — sorts into exactly one class:
178
191
  `.em`/`em export` for a spec/internal finding). This is what the annotation is *for*: a
179
192
  reasoned, ratified deviation stops being re-reported as drift on every run. Cite the
180
193
  annotation text, not fresh judgment — that judgment already happened when it was written.
194
+ - **Unpropagated delta** (MIL-85) — the slice's `doc.driftSignal` (`em export --json`) is
195
+ `"unpropagated-delta"`: a ratified version bump hasn't shipped yet, so `implementedIn` still
196
+ names prior work on purpose. Distinct from Accepted divergence — this is a structurally-
197
+ derived, deterministic signal (`status`/`implementedIn` coherence), not a human-authored
198
+ `divergence` annotation; keep the two separate so a reader can tell which mechanism produced
199
+ which finding. Cite `slice.doc.driftSignal` and `slice.doc.version` together as evidence.
181
200
  - **Extraction uncertainty** — no evidence either way. Never reported as drift or a gap.
182
201
 
183
202
  Every classification must cite the evidence recorded in step 2 (or the `em diff` entry, for
@@ -203,8 +222,11 @@ End of run: update the state file's `Last conformance:` marker, exact format:
203
222
  ```
204
223
 
205
224
  The revision is the one you just diffed against, so the next run's scope starts from here —
206
- step 1 parses this line, so keep the format exact. If any proposals were ratified and applied,
207
- log a Decisions entry noting what changed and why.
225
+ step 1 parses this line, so keep the format exact. **The marker only advances with the human
226
+ in the loop**: in an interactive session, update it after walking the report with the user; a
227
+ headless/scheduled run writes the report but never the marker (nobody ratified the outcome),
228
+ so the next run re-walks the same span — see `docs/ci.md`. If any proposals were ratified and
229
+ applied, log a Decisions entry noting what changed and why.
208
230
 
209
231
  ## Conventions
210
232
 
@@ -10,20 +10,61 @@ Keep `.em` files focused on **structure**; put deep design in markdown linked vi
10
10
 
11
11
  ## CLI
12
12
 
13
+ <!-- GENERATED:cli:start -- run `npm run docs:generate` to refresh, do not hand-edit -->
13
14
  ```bash
14
- em init [file] # scaffold a starter model.em (default: model.em); -f to overwrite
15
- em render <file> -o out.svg # render (extension picks format: svg, png, pdf)
16
- em render <file> --emit-dot # print the Graphviz DOT instead of rendering
17
- em render <file> --keep-empty-lanes # keep the API lane even when empty
18
- em render <file> --slice "<slice-name>" -o slices/<slug>.svg # this slice's own diagram
19
- em watch <file> -o out.svg # re-render on every save (file-based)
20
- em watch <file> -o out.svg --serve # + localhost live viewer, instant SSE push-reload (--port N)
21
- em validate <file> # check event-modeling rules; exit 0 if only warnings/clean
22
- em validate <file> --list-issues # print only open `issue "..."` clauses (slice, element, line, text)
23
- em validate <file> --list-divergences # print only `divergence "..."` clauses (slice, element, line, text)
24
- em validate <file> --list-public # print only events marked `public` (slice, name, line)
25
- em validate <file> --fail-on-issues # opt-in CI gate: exit non-zero while any issue remains open
15
+ em --version # print the installed em version
16
+ em init [file] # scaffold a starter .em model
17
+ em init [file] -f, --force # overwrite if the file exists
18
+ em render <file> # transpile a model and render it (or emit DOT)
19
+ em render <file> -o, --out <path> # output path (extension picks the format)
20
+ em render <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
21
+ em render <file> --slice <name> # render only this slice, redrawn in its own canonical pattern shape (default out: slices/<kebab-slug>.svg)
22
+ em render <file> --emit-dot # print the generated DOT instead of rendering
23
+ em render <file> --keep-empty-lanes # keep the API lane even when empty
24
+ em export <file> # export a versioned JSON snapshot of the normalized model
25
+ em export <file> -o, --out <path> # write to a file instead of stdout
26
+ em diff <old> [new] # compare two models structurally (two files, or one file across git revisions)
27
+ em diff <old> [new] --from <rev> # diff <old> against this git revision instead of a second file
28
+ em diff <old> [new] --to <rev> # diff against this git revision instead of the current file (requires --from)
29
+ em diff <old> [new] --exit-code # exit 1 if the models differ, 0 if identical (git-diff convention)
30
+ em diff <old> [new] --json # print a JSON document instead of the text report (see docs/cli.md)
31
+ em glossary <files> # cross-model glossary of terms, with consistency checks across models (see docs/cli.md)
32
+ em glossary <files> --json # print the full glossary document instead of the text report
33
+ em glossary <files> -o, --out <path> # write the JSON document to a file instead of stdout (requires --json)
34
+ em glossary <files> --list-conflicts # print only the conflict lines, no summary
35
+ em glossary <files> --fail-on-conflicts # exit non-zero if any cross-model term conflicts were found (opt-in — conflicts are warnings and don't block by default)
36
+ em catalog <files> # generate a browsable static HTML catalog site over one or more .em models (see docs/cli.md)
37
+ em catalog <files> -o, --out <dir> # output directory
38
+ em catalog <files> -T, --format <fmt> # diagram format embedded in the catalog (svg or png)
39
+ em catalog <files> --title <text> # catalog site title
40
+ em catalog <files> --keep-empty-lanes # keep the API lane even when empty
41
+ em changelog <file> # render a model's git history as a business-readable ledger (see docs/cli.md)
42
+ em changelog <file> --from <rev> # start the walk at this revision (inclusive)
43
+ em changelog <file> --to <rev> # end the walk at this revision (inclusive; default HEAD)
44
+ em changelog <file> -o, --out <path> # write to a file instead of stdout
45
+ em watch <file> # re-render on every save
46
+ em watch <file> -o, --out <path> # output path (extension picks the format)
47
+ em watch <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
48
+ em watch <file> --keep-empty-lanes # keep the API lane even when empty
49
+ em watch <file> --serve # serve a live viewer with instant push-reload (no polling)
50
+ em watch <file> --port <n> # port for --serve (default 5173)
51
+ em validate <file> # check a model against event-modeling rules
52
+ em validate <file> --list-issues # print only open `issue` diagnostics (slice, element, line, text)
53
+ em validate <file> --list-divergences # print only accepted-divergence annotations (slice, element, line, text) — never fails the build
54
+ em validate <file> --list-public # print only events and views marked `public` (slice, kind, name, line) — an integration-surface audit, never fails the build
55
+ em validate <file> --fail-on-issues # exit non-zero if the model has any open `issue`s (opt-in — issues are warnings and don't block by default)
56
+ em validate <file> --slice-ready <key> # readiness gate for one slice (export key): status ready-to-implement, doc resolvable via note binding, zero unchecked Open Questions — exits non-zero if not ready (MIL-87)
57
+ em ledger <file> # check slice docs' version: field agrees with their content across two git revisions (opt-in CI check, MIL-89 — never part of `em validate`, see docs/ci.md)
58
+ em ledger <file> --from <rev> # baseline revision
59
+ em ledger <file> --to <rev> # compare revision (default: current working tree)
60
+ em ledger <file> --json # print a JSON document instead of the text report (see docs/cli.md)
61
+ em skill install # copy the event-modeling skill into .claude/skills/event-modeling/
62
+ em skill install -f, --force # overwrite an existing installation
63
+ em skill sync [path] # update the vendored .claude/skills/event-modeling/ copy in [path] to match the installed em package (overwrites unconditionally; local edits are never merged, MIL-93)
64
+ em skill check [path] # check the vendored .claude/skills/event-modeling/ copy in [path] for drift against the installed em package; exits non-zero on any mismatch (CI-ready, MIL-93)
65
+ em skill check [path] --json # print a JSON document instead of the text report (see docs/cli.md)
26
66
  ```
67
+ <!-- GENERATED:cli:end -->
27
68
 
28
69
  Install if missing: `npm i -g @milehimikey/em`. PNG works with no system deps; PDF needs
29
70
  `rsvg-convert`.
@@ -57,12 +98,14 @@ type Name { field: Type, ... } # named structured type, reusable from any f
57
98
  |---|---|---|---|---|
58
99
  | `ui` | persona | screen / interface | `@Persona` | `note`, `issue`, `divergence`, `{ fields }` |
59
100
  | `command` | API | state-changing request | — | `note`, `issue`, `divergence`, `{ fields }` |
60
- | `view` | API | read model / projection | — | `from "Event"…`, `note`, `issue`, `divergence`, `{ fields }` |
101
+ | `view` | API | read model / projection | — | `from "Event"…`, `note`, `issue`, `divergence`, `public`, `{ fields }` |
61
102
  | `event` | context | recorded fact (past tense) | `@Context` | `note`, `issue`, `divergence`, `public`, `{ fields }` |
62
103
  | `processor` / `automation` / `saga` / `translation` | automation | system reaction / adapter | — | `from "…"`, `note`, `issue`, `divergence`, `{ fields }` |
63
104
 
64
105
  ### Clauses
65
- - **Tags:** `@Persona` only on `ui`; `@Context` only on `event`. Undeclared tags auto-create a row.
106
+ - **Tags:** `@Persona` only on `ui`; `@Context` only on `event`. Undeclared tags auto-create a
107
+ row. Multi-word tags need no quoting — the tag captures everything after `@` to end of line
108
+ (`ui Ticket Queue @Customer Service` matches `persona Customer Service`).
66
109
  - **`from "X"`** on views/automations declares the source(s); names are quoted, comma-separated.
67
110
  Matching is case-insensitive and whitespace-normalized. A `from` may never point at an event or
68
111
  view that first appears in a LATER slice (forward-only timeline — validation error).
@@ -95,13 +138,16 @@ type Name { field: Type, ... } # named structured type, reusable from any f
95
138
  Raises **no** `em validate` warning by design; use `--list-divergences` to audit. `em diff
96
139
  --json` carries it forward as `acceptedDivergence` on the affected change/removal entry, so
97
140
  `conform` can cite an already-ratified deviation instead of re-flagging it as drift every run.
98
- - **`public`** (events only): marks this event as part of the published integration surface
99
- (e.g. an AsyncAPI contract), as opposed to an internal-only fact. Plain structural flag, no
100
- free text and no diagram marker (same posture as `again`). Write it as the last token on the
101
- line, optionally right before a trailing `@Context` — `event X @Context public` or
102
- `event X public @Context` both work. `em export` carries it as `public: true`/`false`;
103
- `em diff` reports a flip as `event marked public`/`event unmarked public`;
104
- `em validate --list-public` audits which events are public.
141
+ - **`public`** (events and views): marks the element as part of the published integration
142
+ surface — a published event contract (e.g. AsyncAPI), or a view whose read API/response
143
+ shape external consumers depend on — as opposed to an internal-only fact or projection.
144
+ Plain structural flag, no free text and no diagram marker (same posture as `again`). Write
145
+ it as the last token on the line; on an event, optionally right before a trailing
146
+ `@Context` — `event X @Context public` or `event X public @Context` both work. A `public`
147
+ element is exempt from the unread-event / unconsumed-view warnings (its consumer is outside
148
+ this model). `em export` carries it as `public: true`/`false`; `em diff` reports a flip as
149
+ `event marked public`/`event unmarked public` (the entry cites the element's kind, so a
150
+ view flip reads `view "X"`); `em validate --list-public` audits the whole public surface.
105
151
  - **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
106
152
  one-per-line. Types are free text (no semantic checking) UNLESS the type string names a
107
153
  declared `type` (see Named types below), in which case it resolves to a structured
@@ -334,6 +380,12 @@ slice "Read Quote — created" {
334
380
  (`type Node { child: Node }`, or the same shape across several types). Break the cycle, or
335
381
  route the self/mutual reference through an array (`children: Node[]`) if the data is
336
382
  genuinely tree-shaped.
383
+ 9. **Lineage-ref errors** (slice-doc frontmatter, fs-aware) — a
384
+ `split-from`/`merged-from`/`superseded-by` ref with malformed `<slice-key>@v<N>` grammar, a
385
+ self-reference or cycle, a `superseded-by` naming a slice absent from the current model, or
386
+ a ref naming a version higher than the target's own `version:`. A `split-from`/`merged-from`
387
+ naming a key absent from the tree is deliberately silent — that's the normal state after a
388
+ real split/merge (see the four `lineage-*` codes in the table below).
337
389
 
338
390
  **Warnings (should fix):**
339
391
  1. **Automation/translation shares slice with its command** — both `automation`/`processor` and
@@ -348,8 +400,10 @@ slice "Read Quote — created" {
348
400
  4. **Command without event** — every command should record at least one event.
349
401
  5. **Event nobody reads** — the mirror of (4): recording an event no read model projects is a
350
402
  write with no reader. Follow every write slice with the read slice that consumes its event.
351
- Counts as read when a `view` names it in `from` (any `again` instance will do), when a
352
- fieldless-`from` view sits in its slice, or via an explicit `event -> view` arrow.
403
+ Counts as read when a `view` names it in `from` (any `again` instance will do), when a view
404
+ with no `from` of its own sits in its slice, or via an explicit `event -> view` arrow.
405
+ (Events marked `public` are exempt — their reader is outside the model; same for a
406
+ `public` view and the no-consumer warning.)
353
407
  6. **Read model without source** — add `from "Event"` or place the view in a slice with an event.
354
408
  7. **Duplicate name** — the same name defined N times; references resolve to the first. Rename.
355
409
  (A duplicate `type` name always warns, unlike a duplicate element name — there's no
@@ -364,11 +418,73 @@ slice "Read Quote — created" {
364
418
  command in the same slice. Only checked once BOTH the event and at least one same-slice
365
419
  command declare `{ fields }`. This is the payoff of the fields feature for slicing rigor:
366
420
  once fields are written down, `em validate` checks that data flows forward consistently.
421
+ 11. **Event with no producing command** — a fact with no traceable cause; add the command that
422
+ records it (either order within the slice), or an explicit `arrow` from one.
423
+ 12. **Two same-named `translation`s reading different producers** — rename one; fires
424
+ unconditionally (unlike the duplicate-name warning), since the confusion is in reading
425
+ the model, not resolving a reference.
426
+ 13. **Frontmatter coherence** (slice docs, fs-aware) — `status: implemented` with no
427
+ `implementedIn` link at all. The re-ratification mismatch (status flipped back while
428
+ `implementedIn` names the prior PR) is deliberately never flagged — it's the drift
429
+ signal, not incoherence.
430
+ 14. **Doc-join problems** (`em export`/`--slice-ready`) — `binding-missing-file` (a
431
+ `note "slices/<key>.md"` binding pointing at a file that doesn't exist) and
432
+ `frontmatter-invalid` (the doc exists but lacks a frontmatter block or a required key).
367
433
 
368
434
  `divergence "text"` is deliberately NOT in this list — it raises no warning at all, since it
369
435
  records a deviation already reasoned through and accepted, not something to fix. Use
370
436
  `em validate --list-divergences` to audit them on demand.
371
437
 
438
+ ### Full rule code reference
439
+
440
+ Every rule above (and every fs-aware check `em validate` layers on top — lineage, frontmatter
441
+ coherence) has a stable `code`, generated below from the same registry `em` itself validates
442
+ against (`src/model/rules.ts`) — a new rule shows up here the moment it's registered, whether or
443
+ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excluded; see
444
+ `docs/cli.md#em-validate-file` in the
445
+ [em repository](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-validate-file).
446
+
447
+ <!-- GENERATED:validate-rules:start -- run `npm run docs:generate` to refresh, do not hand-edit -->
448
+ | Code | Severity | Title | Fix |
449
+ |---|---|---|---|
450
+ | `arrow-backward` | error | Backward arrow | Restructure so the target comes later. |
451
+ | `arrow-unresolved-source` | error | Arrow source unresolved | Fix the arrow's source name. |
452
+ | `arrow-unresolved-target` | error | Arrow target unresolved | Fix the arrow's target name. |
453
+ | `automation-shares-slice-with-command` | warning | Automation shares slice with its command | Put the triggered command in the next slice. |
454
+ | `binding-missing-file` | warning | Doc binding points at a missing file | Create the slice doc, or fix the `note` path. |
455
+ | `both-ends-of-a-flow/command-no-event` | warning | Command without event | Add the event this command records. |
456
+ | `both-ends-of-a-flow/command-untriggered` | warning | Command with no trigger | Add a `ui` in this slice, or a reaction in the previous slice. |
457
+ | `both-ends-of-a-flow/event-unproduced` | warning | Event with no producing command | Add the command that records it, or an explicit arrow from one. |
458
+ | `both-ends-of-a-flow/event-unread` | warning | Event nobody reads | Project it into a view, or reconsider recording it. |
459
+ | `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. |
460
+ | `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. |
461
+ | `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. |
462
+ | `duplicate-element-ref` | warning | Duplicate element ref | Rename the element so its export ref is unique. |
463
+ | `duplicate-name` | warning | Duplicate name | Rename one of the duplicates. |
464
+ | `duplicate-slice-name` | warning | Duplicate slice name | Rename the slice so its export key is unique. |
465
+ | `duplicate-type-name` | warning | Duplicate type name | Rename one of the duplicate `type` declarations. |
466
+ | `duplicate-type-ref` | warning | Duplicate type ref | Rename the type so its export ref is unique. |
467
+ | `fields-completeness/event-field-no-source` | warning | Event field not from a command | Add the field to a command in the slice, or remove it from the event. |
468
+ | `fields-completeness/view-field-no-source` | warning | View field with no source | Add the field to a source event, or remove it from the view. |
469
+ | `frontmatter-coherence-implemented-without-link` | warning | Implemented without a link | Add `implementedIn` once the slice ships. |
470
+ | `frontmatter-invalid` | warning | Invalid or missing frontmatter | Add the required frontmatter keys, or add a frontmatter block. |
471
+ | `grid-collision` | error | Band collision | Split the colliding elements into separate slices. |
472
+ | `lineage-forward-dangling` | error | Dangling forward lineage ref | Fix the key, or remove the stale successor. |
473
+ | `lineage-ref-cycle` | error | Lineage cycle | Break the cycle — a slice can't be its own ancestor. |
474
+ | `lineage-ref-malformed` | error | Malformed lineage ref | Fix the value to `<slice-key>@v<N>`, or remove it. |
475
+ | `lineage-version-impossible` | error | Impossible lineage version | Fix the referenced version, or ratify the target slice first. |
476
+ | `open-issue` | warning | Open issue | Resolve the question, then remove the `issue` clause. |
477
+ | `reaction-from-future-view` | error | Backward timeline (reaction reads a future view) | Declare the view in or before the reaction's slice. |
478
+ | `reaction-from-unresolved` | error | Unknown read-model source | Project the event into a view first, or fix the `from` reference. |
479
+ | `translation-name-collision` | warning | Translation name reused for different producers | Use a distinct name per producer to avoid confusion. |
480
+ | `type-cycle` | error | Cyclic type reference | Break the cycle, or route the self/mutual reference through an array. |
481
+ | `ui-shares-slice-with-automation` | warning | `ui` shares slice with a reaction, no command | Move the `ui` to the read-model slice, or to the slice with the command this triggers. |
482
+ | `view-again-without-earlier` | error | `again` without an earlier declaration | Declare the view plainly the first time it appears. |
483
+ | `view-from-future-event` | error | Backward timeline (view reads a future event) | Move the source to a later `view X again` instance. |
484
+ | `view-from-unresolved` | error | Unknown event source | Fix the `from` reference to name an existing event. |
485
+ | `view-no-source` | warning | Read model without source | Add `from "Event"`, or place the view in a slice with an event. |
486
+ <!-- GENERATED:validate-rules:end -->
487
+
372
488
  **Design rules that keep models valid:**
373
489
  - One element per band per slice (multiple personas/contexts are fine — they're different rows).
374
490
  - Every `command` slice includes its `event`. Every `view` has a `from` source.
@@ -124,7 +124,8 @@ accepted case, and each one is worth naming explicitly as a finding); every unkn
124
124
  `# TBD`; and the user has confirmed the model reflects today's behavior. Then:
125
125
 
126
126
  1. State file: set phase progress (extraction rounds checked), log the completion decision.
127
- Seed `README.md`'s **slice index** (every slice, pattern, status `none`).
127
+ Seed `README.md`'s **slice index** (every slice, pattern, status "no doc yet" — no slice
128
+ docs exist until the `slice` phase writes them).
128
129
  2. The extracted model **replaces `discover`** — suggest `/event-modeling model` next
129
130
  (steps 5-7: swimlanes, patterns, completeness). Desired-state changes belong *after* the
130
131
  as-is model is agreed — as normal `model`/`slice` evolution, clearly separated from
@@ -0,0 +1,172 @@
1
+ # Implementing a ratified slice
2
+
3
+ The contract for the implementation half of the workflow: an agent (or engineer) turning **one
4
+ ratified slice** into merged, verified code. The facilitation phases (`discover`/`extract` →
5
+ `model` → `slice`) produce the inputs; this document governs consuming them. It applies to any
6
+ implementing agent, whether or not the session started from `/event-modeling`.
7
+
8
+ **The contract in one paragraph:** the slice doc is the spec — nothing between it and the code
9
+ is a source of truth. Verify readiness mechanically before writing any code; implement exactly
10
+ what the doc says; put every gap in front of a human instead of deciding it silently; never
11
+ edit the ratified doc except the two lifecycle fields at merge; prove the work with tests
12
+ traceable to the doc's invariants and scenarios.
13
+
14
+ Why the rules are shaped this way: **agents propose, humans ratify.** Everything in the model
15
+ and its slice docs is there because a human decided it on purpose — that is what makes the
16
+ model authoritative. An implementing agent that silently patches a gap, or edits a ratified
17
+ doc outside a session, converts the model into just another generated artifact and voids the
18
+ whole methodology. (The full human/agent partition is `docs/process.md` in the
19
+ [em repository](https://github.com/milehimikey/em/blob/main/docs/process.md) — not vendored
20
+ with this skill.)
21
+
22
+ ## 1. Gate: verify readiness before starting
23
+
24
+ ```bash
25
+ em validate <model>.em --slice-ready <slice-key>
26
+ ```
27
+
28
+ `<slice-key>` is the slice's export key — the kebab-case slug of its name (`"Place Order"` →
29
+ `place-order`). Exit 0 means: the slice has a doc bound via `note "slices/<key>.md"`, its
30
+ frontmatter is usable, `status: ready-to-implement`, every `## Open Questions` checkbox is
31
+ checked, and no status/version/link incoherence is flagged.
32
+
33
+ **Non-zero exit means stop.** Report which checks failed and hand the slice back to the humans.
34
+ Never make the gate pass yourself — checking an open-question box, flipping `status`, or
35
+ editing frontmatter are ratification decisions, and ratification happens in a facilitated
36
+ session, not in an implementation branch.
37
+
38
+ ## 2. Read the spec
39
+
40
+ Read `slices/<slice-key>.md` end to end — every section is load-bearing:
41
+
42
+ | Section | What it is to the implementer |
43
+ |---|---|
44
+ | frontmatter `pattern:` | Which of the four patterns' implementation shapes to build (State Change / State View / Automation / Translation) |
45
+ | Command / Event field tables | The contracts — names, types, validation rules, immutable-fact markers |
46
+ | `## Invariants / Business Rules` (INV-n IDs) | The non-negotiable rules; every one needs a test citing its ID |
47
+ | `## Scenarios (Given / When / Then)` | The acceptance tests, already written — compile them, don't reinvent them |
48
+ | `## Alternate & Error Flows` | Retries, idempotency, compensations — real requirements, not appendix |
49
+ | `## Non-Functional Requirements` | Authz, PII, performance — implement or consciously surface, never skip |
50
+ | `## Dependencies & Read Models Affected` | The blast radius — what else to check before and after |
51
+
52
+ For timeline context, read the slice's surroundings in the `.em` (what triggers it, what
53
+ consumes its output) — the slice's own diagram (`slices/<slice-key>.svg`) shows its canonical
54
+ pattern shape. For machine-readable facts, use `em export <model>.em` rather than parsing the
55
+ DSL or the doc's frontmatter yourself: it carries the slice's `pattern`, element refs, fields,
56
+ and the joined doc metadata (`slice.doc.status`, `version`, `driftSignal`).
57
+
58
+ If the project ships pattern-specific implementation skills (for example an Axon/DCB skill
59
+ set keyed on a slice doc's `pattern:` frontmatter), route by the slice's pattern and follow
60
+ those.
61
+
62
+ ## 3. Two modes: first version vs. ratified delta
63
+
64
+ Check the doc's `version:` and `## Delta` section (or `em export`'s `slice.doc.driftSignal`):
65
+
66
+ - **First implementation** (`version: 1`, no `## Delta`, `driftSignal: never-implemented`):
67
+ green-field slice implementation. Build the whole pattern shape the doc specifies.
68
+ - **Ratified delta** (`version` > 1, `## Delta` present, `status: ready-to-implement` while
69
+ `implementedIn` still names the prior version's PR — `driftSignal: unpropagated-delta`):
70
+ this is a **change to existing, owned code**. The `## Delta` section's typed operations
71
+ (Added/Modified/Removed/Renamed, each with scenarios) scope the work precisely — which
72
+ invariants, which scenarios, which tests change. Touch what the delta names; leave the rest.
73
+ **Never regenerate merged code wholesale from the model** — after merge, code is owned by
74
+ the people who've edited it (generated-then-owned); regeneration is legal only for slices
75
+ whose code never merged.
76
+
77
+ ## 4. Gaps: propose, never decide
78
+
79
+ Mid-implementation you will hit things the doc doesn't answer — a field's edge case, an
80
+ undocumented ordering, a contradiction with adjacent code. The discipline:
81
+
82
+ - **Stop that thread and surface it.** State precisely what's unspecified, the options, and
83
+ your recommendation. The human resolves it — at the model, where the resolution lands as an
84
+ answered open question or a small ratified delta alongside your fix, so the decision is
85
+ recorded instead of buried in an implementation diff.
86
+ - **Never edit the ratified doc to record your own answer**, and never quietly pick a behavior
87
+ a business person could have an opinion on. Silent divergence is the failure mode the whole
88
+ conformance loop exists to catch — don't manufacture it.
89
+ - Purely technical choices with no behavioral surface (private naming, idiomatic structure,
90
+ which assertion library) are yours; anything observable in behavior, data, or contract is not.
91
+
92
+ ## 5. Definition of done
93
+
94
+ - Every `INV-n` invariant has at least one test that cites its ID.
95
+ - Every scenario in `## Scenarios (Given / When / Then)` exists as a passing test; rejection
96
+ scenarios assert the doc's named rejection reason.
97
+ - Alternate/error flows (idempotency included) are covered by tests.
98
+ - The build and full test suite are green; the model still validates (`em validate` in CI —
99
+ you didn't touch the `.em`, so this only fails if something else broke).
100
+ - Nothing between the slice doc and the code was committed as a source of truth (see §7 —
101
+ work containers are ephemeral; generated or symlinked specs are renderings).
102
+
103
+ ## 6. At merge: the lifecycle flip
104
+
105
+ When the PR merges, exactly **two** frontmatter fields change on the slice doc:
106
+
107
+ ```yaml
108
+ status: implemented
109
+ implementedIn: <PR or commit URL>
110
+ ```
111
+
112
+ This is supply-loop mechanics, not ratification — it's the one edit an implementing agent makes
113
+ to a slice doc, and it's why `em ledger` deliberately excludes these two fields from what
114
+ counts as content. **Do not bump `version:`** — versions bump only when a delta is ratified;
115
+ a bump here is a ledger defect. In bridge-equipped projects,
116
+ `npx -p em-sdd-bridge em-sdd-mark-implemented <slice-key> <pr-url>` does the flip for you.
117
+
118
+ Then update the model's `README.md` slice index if the project keeps one, so the one canonical
119
+ slice table reflects the new status and link. The `implementedIn` link is what the `conform`
120
+ phase later uses to anchor drift-checking — leaving it empty blinds the loop.
121
+
122
+ ## 7. Spec-kit projects: the SDD adapter
123
+
124
+ If the repository uses spec-kit (a `.specify/` directory exists), do not hand-author spec-kit
125
+ artifacts — allocate through **em-sdd-bridge**, redirect mode preferred:
126
+
127
+ ```bash
128
+ npx em-sdd-bridge@<pinned-version> <slice-key> --symlink
129
+ ```
130
+
131
+ That allocates the feature branch and `specs/NNN-slug/` dir, runs the same readiness gates,
132
+ and drops `spec.md` as a **symlink to the slice doc** — the slice doc itself travels through
133
+ plan/tasks as FEATURE_SPEC. The rules that keep redirect mode safe:
134
+
135
+ - **Never run `/speckit.specify`.** The ratified slice *is* the spec; an interactive specify
136
+ session against a defined slice creates a second, unratified source of truth.
137
+ - **`/speckit.clarify` must never write into a slice doc.** Open questions resolve at the
138
+ model (that's what the readiness gate enforced); a ratified slice has nothing to clarify.
139
+ Projects using em-sdd-preset's overlays carry a mechanical guard for this — respect the
140
+ rule even where the guard isn't installed.
141
+ - `/speckit.plan` and `/speckit.tasks` may run against the slice doc directly; the preset's
142
+ redirect preambles map spec-kit's section vocabulary onto the slice doc's sections.
143
+ - **Emission fallback**: where redirect can't work (Windows checkouts without symlink
144
+ privileges, two-doc pattern-pair bundles, a toolchain that mutates its spec files), run the
145
+ bridge without `--symlink` to render `spec.md` from the slice doc. The generated file is a
146
+ rendering — never hand-edit it; regenerate it from the slice doc instead.
147
+ - Everything under `specs/NNN-*/` is an **ephemeral work container** — one per change effort,
148
+ discardable; the PR is the durable record of the effort, the slice doc the durable
149
+ definition of the behavior.
150
+
151
+ No spec-kit (or any SDD tool)? Implement straight from the slice doc — it already contains
152
+ everything a spec holds. Don't introduce an intermediate spec document of your own.
153
+
154
+ ## 8. Never do
155
+
156
+ | Rule | Because |
157
+ |---|---|
158
+ | Never implement from a slice that fails `--slice-ready` | The gate is the handoff contract; bypassing it hands you unratified decisions |
159
+ | Never edit a ratified slice doc, except the two §6 fields at merge | Doc content is ratified; edits outside a session are unratified decisions |
160
+ | Never edit the `.em` model | Model edits are ratified decisions — propose, don't write |
161
+ | Never bump `version:` | Versions move only with ratified deltas; see `em ledger` |
162
+ | Never silently decide unspecified behavior | Silent divergence is the disease the conformance loop exists to catch |
163
+ | Never regenerate merged code from the model | Generated-then-owned: post-merge code belongs to its owners |
164
+ | Never commit an authored intermediate spec | The slice is the spec; anything between it and the code is a rendering |
165
+
166
+ ## 9. Afterward: the loop closes
167
+
168
+ Implementation isn't the end of the slice's story. On a cadence — or whenever someone asks —
169
+ the `conform` phase checks implemented slices against the code, using the `implementedIn` link
170
+ you recorded, and reports drift for humans to rule on (see `reference/conform.md`). Your two
171
+ duties to that future loop are already behind you if you followed this doc: an accurate
172
+ lifecycle flip, and zero silent decisions.