@milehimikey/em 1.7.1 → 1.8.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 (112) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +92 -65
  2. package/.claude/skills/event-modeling/reference/conform.md +45 -20
  3. package/.claude/skills/event-modeling/reference/em-dsl.md +120 -55
  4. package/.claude/skills/event-modeling/reference/extract.md +5 -4
  5. package/.claude/skills/event-modeling/reference/implement.md +34 -22
  6. package/.claude/skills/event-modeling/templates/model-readme.md +8 -5
  7. package/.claude/skills/event-modeling/templates/slice.md +19 -5
  8. package/README.md +6 -4
  9. package/dist/catalog/docJoin.d.ts +28 -3
  10. package/dist/catalog/docJoin.js +82 -13
  11. package/dist/catalog/docJoin.js.map +1 -1
  12. package/dist/catalog/docModelConsistencyValidate.d.ts +8 -0
  13. package/dist/catalog/docModelConsistencyValidate.js +335 -0
  14. package/dist/catalog/docModelConsistencyValidate.js.map +1 -0
  15. package/dist/catalog/noteBindingValidate.d.ts +8 -0
  16. package/dist/catalog/noteBindingValidate.js +133 -0
  17. package/dist/catalog/noteBindingValidate.js.map +1 -0
  18. package/dist/catalog/pages.js +3 -3
  19. package/dist/catalog/pages.js.map +1 -1
  20. package/dist/catalog/sliceDoc.d.ts +14 -0
  21. package/dist/catalog/sliceDoc.js +22 -0
  22. package/dist/catalog/sliceDoc.js.map +1 -1
  23. package/dist/catalog/sliceReadyValidate.d.ts +18 -0
  24. package/dist/catalog/sliceReadyValidate.js +25 -1
  25. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  26. package/dist/cli/agentsMd.d.ts +15 -0
  27. package/dist/cli/agentsMd.js +72 -0
  28. package/dist/cli/agentsMd.js.map +1 -0
  29. package/dist/cli/conformScope.d.ts +86 -0
  30. package/dist/cli/conformScope.js +156 -0
  31. package/dist/cli/conformScope.js.map +1 -0
  32. package/dist/cli/contract.d.ts +7 -0
  33. package/dist/cli/contract.js +27 -0
  34. package/dist/cli/contract.js.map +1 -0
  35. package/dist/cli/coverage.d.ts +69 -0
  36. package/dist/cli/coverage.js +157 -0
  37. package/dist/cli/coverage.js.map +1 -0
  38. package/dist/cli/markImplemented.d.ts +34 -0
  39. package/dist/cli/markImplemented.js +180 -0
  40. package/dist/cli/markImplemented.js.map +1 -0
  41. package/dist/cli/migrateReactionShape.d.ts +62 -0
  42. package/dist/cli/migrateReactionShape.js +283 -0
  43. package/dist/cli/migrateReactionShape.js.map +1 -0
  44. package/dist/cli/sliceIndex.d.ts +49 -0
  45. package/dist/cli/sliceIndex.js +115 -0
  46. package/dist/cli/sliceIndex.js.map +1 -0
  47. package/dist/cli/sliceNew.d.ts +18 -0
  48. package/dist/cli/sliceNew.js +53 -0
  49. package/dist/cli/sliceNew.js.map +1 -0
  50. package/dist/cli/stateFile.d.ts +74 -0
  51. package/dist/cli/stateFile.js +191 -0
  52. package/dist/cli/stateFile.js.map +1 -0
  53. package/dist/cli.js +535 -43
  54. package/dist/cli.js.map +1 -1
  55. package/dist/emit/coverageJson.d.ts +8 -0
  56. package/dist/emit/coverageJson.js +30 -0
  57. package/dist/emit/coverageJson.js.map +1 -0
  58. package/dist/emit/dot.js +41 -10
  59. package/dist/emit/dot.js.map +1 -1
  60. package/dist/emit/json.d.ts +72 -4
  61. package/dist/emit/json.js +73 -4
  62. package/dist/emit/json.js.map +1 -1
  63. package/dist/emit/validateJson.d.ts +52 -0
  64. package/dist/emit/validateJson.js +120 -0
  65. package/dist/emit/validateJson.js.map +1 -0
  66. package/dist/mcp/main.d.ts +2 -0
  67. package/dist/mcp/main.js +20 -0
  68. package/dist/mcp/main.js.map +1 -0
  69. package/dist/mcp/server.d.ts +11 -0
  70. package/dist/mcp/server.js +260 -0
  71. package/dist/mcp/server.js.map +1 -0
  72. package/dist/model/model.d.ts +30 -1
  73. package/dist/model/model.js +26 -0
  74. package/dist/model/model.js.map +1 -1
  75. package/dist/model/rules.d.ts +114 -0
  76. package/dist/model/rules.js +116 -4
  77. package/dist/model/rules.js.map +1 -1
  78. package/dist/model/validate.js +83 -7
  79. package/dist/model/validate.js.map +1 -1
  80. package/dist/parser/ast.d.ts +41 -0
  81. package/dist/parser/lexer.d.ts +16 -0
  82. package/dist/parser/lexer.js +44 -2
  83. package/dist/parser/lexer.js.map +1 -1
  84. package/dist/parser/parser.js +340 -20
  85. package/dist/parser/parser.js.map +1 -1
  86. package/dist/render/drawNotes.js +8 -1
  87. package/dist/render/drawNotes.js.map +1 -1
  88. package/dist/render/render.js +85 -17
  89. package/dist/render/render.js.map +1 -1
  90. package/dist/render/serve.d.ts +4 -0
  91. package/dist/render/serve.js +23 -227
  92. package/dist/render/serve.js.map +1 -1
  93. package/dist/render/sliceStatus.d.ts +1 -1
  94. package/dist/render/sliceStatus.js +35 -6
  95. package/dist/render/sliceStatus.js.map +1 -1
  96. package/dist/render/viewerHtml.d.ts +1 -0
  97. package/dist/render/viewerHtml.js +5 -0
  98. package/dist/render/viewerHtml.js.map +1 -0
  99. package/dist/render/watch.d.ts +10 -0
  100. package/dist/render/watch.js +29 -0
  101. package/dist/render/watch.js.map +1 -1
  102. package/dist/templates.d.ts +19 -0
  103. package/dist/templates.js +169 -0
  104. package/dist/templates.js.map +1 -1
  105. package/dist/util/markers.d.ts +13 -0
  106. package/dist/util/markers.js +30 -0
  107. package/dist/util/markers.js.map +1 -0
  108. package/dist/util/walkDir.d.ts +10 -1
  109. package/dist/util/walkDir.js +11 -7
  110. package/dist/util/walkDir.js.map +1 -1
  111. package/package.json +12 -3
  112. package/.claude/skills/event-modeling/templates/live.html +0 -105
@@ -12,62 +12,93 @@ Keep `.em` files focused on **structure**; put deep design in markdown linked vi
12
12
 
13
13
  <!-- GENERATED:cli:start -- run `npm run docs:generate` to refresh, do not hand-edit -->
14
14
  ```bash
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)
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 scaffold <name> # scaffold a full project: <slug>/<slug>.em, README.md, .event-modeling.md (see docs/cli.md — for just a starter .em, use `em init`)
19
+ em scaffold <name> -f, --force # overwrite the directory's contents if it already exists
20
+ em render <file> # transpile a model and render it (or emit DOT)
21
+ em render <file> -o, --out <path> # output path (extension picks the format)
22
+ em render <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
23
+ em render <file> --slice <name> # render only this slice, redrawn in its own canonical pattern shape (default out: slices/<kebab-slug>.svg)
24
+ em render <file> --emit-dot # print the generated DOT instead of rendering
25
+ em render <file> --keep-empty-lanes # keep the API lane even when empty
26
+ em export <file> # export a versioned JSON snapshot of the normalized model
27
+ em export <file> -o, --out <path> # write to a file instead of stdout
28
+ em export <file> --slice <key> # export only this slice's object (pattern/fields/doc) instead of the whole model (export key, MIL-128) — refuses only if THIS slice has an error; an unrelated slice's breakage elsewhere in the model doesn't block it (see docs/cli.md)
29
+ em diff <old> [new] # compare two models structurally (two files, or one file across git revisions)
30
+ em diff <old> [new] --from <rev> # diff <old> against this git revision instead of a second file
31
+ em diff <old> [new] --to <rev> # diff against this git revision instead of the current file (requires --from)
32
+ em diff <old> [new] --exit-code # exit 1 if the models differ, 0 if identical (git-diff convention)
33
+ em diff <old> [new] --json # print a JSON document instead of the text report (see docs/cli.md)
34
+ em glossary <files> # cross-model glossary of terms, with consistency checks across models (see docs/cli.md)
35
+ em glossary <files> --json # print the full glossary document instead of the text report
36
+ em glossary <files> -o, --out <path> # write the JSON document to a file instead of stdout (requires --json)
37
+ em glossary <files> --list-conflicts # print only the conflict lines, no summary
38
+ 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)
39
+ em catalog <files> # generate a browsable static HTML catalog site over one or more .em models (see docs/cli.md)
40
+ em catalog <files> -o, --out <dir> # output directory
41
+ em catalog <files> -T, --format <fmt> # diagram format embedded in the catalog (svg or png)
42
+ em catalog <files> --title <text> # catalog site title
43
+ em catalog <files> --keep-empty-lanes # keep the API lane even when empty
44
+ em slice new <name> # scaffold a fresh slices/<key>.md doc — the 5 frontmatter keys required at `status: draft` plus the `# Slice:` heading and diagram-image stub; judgment sections (Intent, Scenarios, Open Questions, ...) stay hand-authored (see docs/slice-doc-schema.md, templates/slice.md)
45
+ em slice new <name> --pattern <pattern> # slice pattern: state-change | state-view | automation | translation
46
+ em slice new <name> --swimlane <swimlane> # swimlane, e.g. "Persona → Context"
47
+ em slice new <name> -f, --force # overwrite the file if it already exists
48
+ em slice index <file> # rewrite the model's sibling README.md's GENERATED Slices table from `em export`'s slice facts (key, pattern, doc status/implementedIn) — the hand-maintained table is deprecated
49
+ em slice index <file> --check # verify the table is current; exit non-zero on drift without writing (CI)
50
+ em slice mark-implemented <file> <slice-key> <pr-url> # flip a slice doc's frontmatter to `status: implemented` / `implementedIn: <pr-url>` — the one edit an implementing agent makes to a ratified doc at merge (MIL-103, replaces the em-sdd-bridge `em-sdd-mark-implemented` script; see reference/implement.md §6). Idempotent on the same URL; refuses to overwrite a different one; never touches `version:` or the doc body
51
+ em changelog <file> # render a model's git history as a business-readable ledger (see docs/cli.md)
52
+ em changelog <file> --from <rev> # start the walk at this revision (inclusive)
53
+ em changelog <file> --to <rev> # end the walk at this revision (inclusive; default HEAD)
54
+ em changelog <file> -o, --out <path> # write to a file instead of stdout
55
+ em state read [dir] # print the state file's mechanical fields as JSON
56
+ em state set-phase <phase> [dir] # rewrite Current phase: (and Last updated:); --step also rewrites Current step:
57
+ em state set-phase <phase> [dir] --step <n> # also set Current step: to this value
58
+ em state set-conformance <revision> [dir] # rewrite Last conformance: (and Last updated:) in the exact format reference/conform.md parses
59
+ em state set-conformance <revision> [dir] --report <path> # path to the conformance report just written
60
+ em state set-review <date> [dir] # rewrite Last stakeholder review: (and Last updated:)
61
+ em conform-scope <file> # mechanize conform phase step 1 (reference/conform.md): map the target repo's changed paths since Last conformance: to slices via each slice doc's implementedIn, JSON to stdout — --seed-asis also seeds the <model>-asis.em scratch model (see docs/cli.md)
62
+ em conform-scope <file> --repo <path> # path to (or inside) the target codebase's git repository
63
+ em conform-scope <file> --full # ignore Last conformance:/changed paths; scope every implemented slice
64
+ em conform-scope <file> --seed-asis # write <model>-asis.em as a byte copy of the canonical model and ensure it's gitignored
65
+ em watch <file> # re-render on every save
66
+ em watch <file> -o, --out <path> # output path (extension picks the format)
67
+ em watch <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
68
+ em watch <file> --keep-empty-lanes # keep the API lane even when empty
69
+ em watch <file> --serve # serve a live viewer with instant push-reload (no polling)
70
+ em watch <file> --port <n> # port for --serve (default 5173)
71
+ em validate <file> # check a model against event-modeling rules
72
+ em validate <file> --list-issues # print only open `issue` diagnostics (slice, element, line, text)
73
+ em validate <file> --list-divergences # print only accepted-divergence annotations (slice, element, line, text) — never fails the build
74
+ em validate <file> --list-public # print only events and views marked `public` (slice, kind, name, line) — an integration-surface audit, never fails the build
75
+ 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)
76
+ 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)
77
+ em validate <file> --json # print a JSON document instead of text — works on a model WITH errors, unlike `em export` (MIL-128, see docs/cli.md); exit codes are unchanged
78
+ em migrate <file> # rewrite the old two-slice Automation/Translation shape into the merged single-slice shape MIL-120 made canonical (see docs/cli.md)
79
+ em migrate <file> --write # apply the rewrite to the file (default: dry run — report only, write nothing)
80
+ 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)
81
+ em ledger <file> --from <rev> # baseline revision
82
+ em ledger <file> --to <rev> # compare revision (default: current working tree)
83
+ em ledger <file> --json # print a JSON document instead of the text report (see docs/cli.md)
84
+ em coverage <file> # check that every INV-* invariant ID cited in a ready-to-implement/implemented slice doc is cited by a test under --tests <dir> (MIL-130) — mechanizes reference/implement.md's definition-of-done citation check; advisory by default, --strict for CI
85
+ em coverage <file> --tests <dir> # directory to scan recursively for test files citing invariant IDs
86
+ em coverage <file> --strict # exit non-zero if any invariant ID has zero citations (CI)
87
+ em coverage <file> --json # print a JSON document instead of the text report (see docs/cli.md)
88
+ em contract # print the packaged implementation contract (reference/implement.md) to stdout — the agent-neutral discovery path for any agent that can run a shell, not just Claude Code (MIL-129); see docs/cli.md
89
+ em mcp # start an MCP (Model Context Protocol) server over stdio, exposing validate/slice_ready/list_markers/export_model/export_slice/coverage/contract as tools (MIL-21) — a structured, agent-facing alternative to shelling out to `em`; see docs/mcp.md. Equivalent to running the `em-mcp` bin directly
90
+ em skill install # copy the event-modeling skill into .claude/skills/event-modeling/
91
+ em skill install -f, --force # overwrite an existing installation
92
+ em skill install --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
93
+ 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)
94
+ em skill sync [path] --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
95
+ 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)
96
+ em skill check [path] --json # print a JSON document instead of the text report (see docs/cli.md)
66
97
  ```
67
98
  <!-- GENERATED:cli:end -->
68
99
 
69
- Install if missing: `npm i -g @milehimikey/em`. PNG works with no system deps; PDF needs
70
- `rsvg-convert`.
100
+ Install if missing: `npm i -g @milehimikey/em`. SVG, PNG, and PDF all work with no system
101
+ deps; only rarer formats (ps, eps, ...) need `rsvg-convert`.
71
102
 
72
103
  ---
73
104
 
@@ -97,9 +128,9 @@ type Name { field: Type, ... } # named structured type, reusable from any f
97
128
  | Keyword | Band | Meaning | Tag | Extra clauses |
98
129
  |---|---|---|---|---|
99
130
  | `ui` | persona | screen / interface | `@Persona` | `note`, `issue`, `divergence`, `{ fields }` |
100
- | `command` | API | state-changing request | — | `note`, `issue`, `divergence`, `{ fields }` |
131
+ | `command` | API | state-changing request | — | `note`, `issue`, `divergence`, `renamed from`, `{ fields }` |
101
132
  | `view` | API | read model / projection | — | `from "Event"…`, `note`, `issue`, `divergence`, `public`, `{ fields }` |
102
- | `event` | context | recorded fact (past tense) | `@Context` | `note`, `issue`, `divergence`, `public`, `{ fields }` |
133
+ | `event` | context | recorded fact (past tense) | `@Context` | `note`, `issue`, `divergence`, `public`, `tag`, `renamed from`, `{ fields }` |
103
134
  | `processor` / `automation` / `saga` / `translation` | automation | system reaction / adapter | — | `from "…"`, `note`, `issue`, `divergence`, `{ fields }` |
104
135
 
105
136
  ### Clauses
@@ -148,6 +179,30 @@ type Name { field: Type, ... } # named structured type, reusable from any f
148
179
  this model). `em export` carries it as `public: true`/`false`; `em diff` reports a flip as
149
180
  `event marked public`/`event unmarked public` (the entry cites the element's kind, so a
150
181
  view flip reads `view "X"`); `em validate --list-public` audits the whole public surface.
182
+ - **`tag`** (events only): declares a DCB (Dynamic Consistency Boundary) tag key. Three forms:
183
+ a trailing `tag` on a field line inside the event's `{ … }` block (`priceId: UUID tag`, or
184
+ typeless `priceId tag`) — an identity tag, key defaults to the field's own name; a bare field
185
+ whose ENTIRE text is just `tag` is a field named `tag`, not a clause. `tag <key> from a, b`
186
+ (composite, ≥2 bare/unquoted field names — unlike a view's quoted `from`). `tag <key>
187
+ external "text"` (external — the string is documentation only, never parsed). Composite/
188
+ external clauses are element-level: write them as a trailing clause on the event (same
189
+ family as `note`/`issue`), or as one or more standalone `tag ...` lines immediately following
190
+ the event inside the slice body (attaches to the most recently declared element, which must
191
+ be an event); multiple accumulate. `tag` anywhere on a command/view/`ui`/`type` field, or an
192
+ element-level `tag` clause on a non-event, is a parse error. `em export` carries every tag
193
+ key under the event's `tags` array (`{ key, kind, fields, description }`); `em validate`
194
+ flags a composite tag naming an unknown field and a duplicate tag key on one event.
195
+ - **`renamed from "Old1", "Old2"`** (events and commands only): records the prior name(s) an
196
+ element, or one of its fields, was known as — codegen metadata for converting
197
+ already-stored payloads instead of an upcaster chain. Element-level trails the element's own
198
+ name (same trailing-clause family as `@Context`/`public`); field-level trails a field's type,
199
+ or the bare name of a typeless field, inside the `{ … }` block — event/command fields only, a
200
+ parse error on a view/`ui`/automation-kind field or a `type` field. The list is quoted,
201
+ comma-separated, most-recent-old-name first. Inside one inline `{ … }` block, a bare quoted
202
+ field name immediately after a renamed field is read as a CONTINUATION of that list, not a
203
+ new field — give the quoted-name field a type, or write fields one per line, to avoid the
204
+ ambiguity. `em export` carries it as `renamedFrom: string[] | null` on both the element and
205
+ each field. **`em diff` does not read this clause** — a rename still reports as remove+add.
151
206
  - **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
152
207
  one-per-line. Types are free text (no semantic checking) UNLESS the type string names a
153
208
  declared `type` (see Named types below), in which case it resolves to a structured
@@ -463,6 +518,10 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
463
518
  | `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. |
464
519
  | `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. |
465
520
  | `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. |
521
+ | `doc-model-element-not-in-doc` | warning | Model element the doc doesn't mention | Add the matching marker to the doc, or remove the element from the model. |
522
+ | `doc-model-element-not-in-model` | warning | Doc names an element the model doesn't have | Add the element to the model, or fix/remove it in the doc. |
523
+ | `doc-model-field-mismatch` | warning | Doc/model field mismatch | Reconcile the field table with the model's fields — names and types. |
524
+ | `doc-model-pattern-mismatch` | warning | Doc/model pattern mismatch | Fix the doc's `pattern:` frontmatter to match the model, or restructure the slice to match the doc. |
466
525
  | `duplicate-element-ref` | warning | Duplicate element ref | Rename the element so its export ref is unique. |
467
526
  | `duplicate-name` | warning | Duplicate name | Rename one of the duplicates. |
468
527
  | `duplicate-slice-name` | warning | Duplicate slice name | Rename the slice so its export key is unique. |
@@ -477,9 +536,15 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
477
536
  | `lineage-ref-cycle` | error | Lineage cycle | Break the cycle — a slice can't be its own ancestor. |
478
537
  | `lineage-ref-malformed` | error | Malformed lineage ref | Fix the value to `<slice-key>@v<N>`, or remove it. |
479
538
  | `lineage-version-impossible` | error | Impossible lineage version | Fix the referenced version, or ratify the target slice first. |
539
+ | `note-binding-dangling` | warning | Dangling cross-slice note | Create the doc at that path, or fix/remove the note. |
540
+ | `note-binding-extra` | warning | Extra doc-binding note, ignored | Remove the note, or point it at the slice's actual bound doc. |
541
+ | `note-binding-unratified` | warning | Unratified cross-slice note | Add `covers: <this-slice-key>` to that doc's frontmatter, or correct the note's path. |
542
+ | `note-binding-unusable` | warning | Cross-slice note to a doc with unusable frontmatter | Fix that doc's frontmatter, or fix/remove the note. |
480
543
  | `open-issue` | warning | Open issue | Resolve the question, then remove the `issue` clause. |
481
544
  | `reaction-from-future-view` | error | Backward timeline (reaction reads a future view) | Declare the view in or before the reaction's slice. |
482
545
  | `reaction-from-unresolved` | error | Unknown read-model source | Project the event into a view first, or fix the `from` reference. |
546
+ | `tag-composite-unknown-field` | error | Composite tag names an unknown field | Fix the field name, or add it to the event's fields. |
547
+ | `tag-duplicate-key` | error | Duplicate tag key | Rename one of the tags so every key on the event is unique. |
483
548
  | `translation-name-collision` | warning | Translation name reused for different producers | Use a distinct name per producer to avoid confusion. |
484
549
  | `type-cycle` | error | Cyclic type reference | Break the cycle, or route the self/mutual reference through an array. |
485
550
  | `ui-shares-slice-with-automation` | warning | `ui` shares slice with a reaction | Move the `ui` to the slice that displays the read model, or drop it. |
@@ -23,8 +23,8 @@ reaction-shares-a-slice-with-its-command shape — still applies.
23
23
 
24
24
  ## Setup
25
25
 
26
- 1. Run the SKILL.md preconditions (tool check, model location) and scaffold the standard
27
- project layout if no model exists yet.
26
+ 1. Run the SKILL.md preconditions (tool check, model location). If no model exists yet, run
27
+ `em scaffold <model-name>` (see SKILL.md's "Project layout this skill creates").
28
28
  2. **Agree the scope line first**: which system, which part, what's out of bounds. Write it to
29
29
  the state file's Session inputs.
30
30
  3. Record in the state file: `Source mode` (after detection, below) and
@@ -126,8 +126,9 @@ accepted case, and each one is worth naming explicitly as a finding); every unkn
126
126
  `# TBD`; and the user has confirmed the model reflects today's behavior. Then:
127
127
 
128
128
  1. State file: set phase progress (extraction rounds checked), log the completion decision.
129
- Seed `README.md`'s **slice index** (every slice, pattern, status "no doc yet" — no slice
130
- docs exist until the `slice` phase writes them).
129
+ Run `em slice index <model-name>.em` to seed `README.md`'s **Slices table** — every slice,
130
+ its AST-derived pattern, status "no doc yet" (no slice docs exist until the `slice` phase
131
+ writes them).
131
132
  2. The extracted model **replaces `discover`** — suggest `/event-modeling model` next
132
133
  (steps 5-7: swimlanes, patterns, completeness). Desired-state changes belong *after* the
133
134
  as-is model is agreed — as normal `model`/`slice` evolution, clearly separated from
@@ -22,18 +22,22 @@ with this skill.)
22
22
  ## 1. Gate: verify readiness before starting
23
23
 
24
24
  ```bash
25
- em validate <model>.em --slice-ready <slice-key>
25
+ em validate <model>.em --slice-ready <slice-key> --json
26
26
  ```
27
27
 
28
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.
29
+ `place-order`). Read the JSON document's `ready` field — don't infer it from the exit code or
30
+ any printed text. `ready: true` means: the slice has a doc bound via `note "slices/<key>.md"`,
31
+ its frontmatter is usable, `status: ready-to-implement`, every `## Open Questions` checkbox is
32
+ checked, and no status/version/link incoherence is flagged — `gates` names each of those 4
33
+ conditions individually (`docBound`/`frontmatterUsable`/`statusReady`/
34
+ `noUncheckedOpenQuestions`) if you need to say which one is blocking.
35
+
36
+ **`ready: false` means stop.** Report which `gates` entries are `false` (and any `diagnostics`
37
+ entries concerning this slice) and hand the slice back to the humans. Never make the gate pass
38
+ yourself — checking an open-question box, flipping `status`, or editing frontmatter are
39
+ ratification decisions, and ratification happens in a facilitated session, not in an
40
+ implementation branch.
37
41
 
38
42
  ## 2. Read the spec
39
43
 
@@ -43,7 +47,7 @@ Read `slices/<slice-key>.md` end to end — every section is load-bearing:
43
47
  |---|---|
44
48
  | frontmatter `pattern:` | Which of the four patterns' implementation shapes to build (State Change / State View / Automation / Translation) |
45
49
  | 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 |
50
+ | `## Invariants / Business Rules` (`INV-<MNEMONIC>-n` IDs) | The non-negotiable rules; every one needs a test citing its ID |
47
51
  | `## Scenarios (Given / When / Then)` | The acceptance tests, already written — compile them, don't reinvent them |
48
52
  | `## Alternate & Error Flows` | Retries, idempotency, compensations — real requirements, not appendix |
49
53
  | `## Non-Functional Requirements` | Authz, PII, performance — implement or consciously surface, never skip |
@@ -51,9 +55,10 @@ Read `slices/<slice-key>.md` end to end — every section is load-bearing:
51
55
 
52
56
  For timeline context, read the slice's surroundings in the `.em` (what triggers it, what
53
57
  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`).
58
+ pattern shape. For machine-readable facts, use `em export <model>.em --slice <slice-key>`
59
+ rather than parsing the DSL or the doc's frontmatter yourself: it returns just this slice's
60
+ object — `pattern`, element refs, fields, and the joined doc metadata (`slice.doc.status`,
61
+ `version`, `driftSignal`) — without piping the whole model's export to find one `slice.doc`.
57
62
 
58
63
  If the project ships pattern-specific implementation skills (for example an Axon/DCB skill
59
64
  set keyed on a slice doc's `pattern:` frontmatter), route by the slice's pattern and follow
@@ -91,7 +96,9 @@ undocumented ordering, a contradiction with adjacent code. The discipline:
91
96
 
92
97
  ## 5. Definition of done
93
98
 
94
- - Every `INV-n` invariant has at least one test that cites its ID.
99
+ - Every `INV-<MNEMONIC>-n` invariant has at least one test that cites its ID — checked mechanically by
100
+ `em coverage <model>.em --tests <dir>` (MIL-130); run it (add `--strict` in CI) rather than
101
+ eyeballing citations by hand.
95
102
  - Every scenario in `## Scenarios (Given / When / Then)` exists as a passing test; rejection
96
103
  scenarios assert the doc's named rejection reason.
97
104
  - Alternate/error flows (idempotency included) are covered by tests.
@@ -112,12 +119,17 @@ implementedIn: <PR or commit URL>
112
119
  This is supply-loop mechanics, not ratification — it's the one edit an implementing agent makes
113
120
  to a slice doc, and it's why `em ledger` deliberately excludes these two fields from what
114
121
  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.
122
+ a bump here is a ledger defect.
123
+
124
+ `em slice mark-implemented <model>.em <slice-key> <pr-url>` does the flip for you (MIL-103) —
125
+ resolves the doc via the same note-binding join `--slice-ready` uses, is idempotent on a re-run
126
+ with the same URL, and refuses (never silently overwrites) if the doc is already `implemented`
127
+ with a different URL. Prefer it over hand-editing the doc's frontmatter.
117
128
 
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.
129
+ Then, if the project keeps a model README (from `templates/model-readme.md`), run
130
+ `em slice index <model-name>.em` so its generated Slices table reflects the new status and
131
+ link — never hand-edit that table. The `implementedIn` link is what the `conform` phase later
132
+ uses to anchor drift-checking — leaving it empty blinds the loop.
121
133
 
122
134
  ## 7. Spec-kit projects: the SDD adapter
123
135
 
@@ -141,9 +153,9 @@ plan/tasks as FEATURE_SPEC. The rules that keep redirect mode safe:
141
153
  - `/speckit.plan` and `/speckit.tasks` may run against the slice doc directly; the preset's
142
154
  redirect preambles map spec-kit's section vocabulary onto the slice doc's sections.
143
155
  - **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.
156
+ privileges, a toolchain that mutates its spec files), run the bridge without `--symlink` to
157
+ render `spec.md` from the slice doc. The generated file is a rendering — never hand-edit it;
158
+ regenerate it from the slice doc instead.
147
159
  - Everything under `specs/NNN-*/` is an **ephemeral work container** — one per change effort,
148
160
  discardable; the PR is the durable record of the effort, the slice doc the durable
149
161
  definition of the behavior.
@@ -10,8 +10,9 @@ em watch {{model-name}}.em -o {{model-name}}.svg --serve # re-render + instant
10
10
  # then open the URL it prints (http://localhost:5173/?svg={{model-name}}.svg) and share the screen
11
11
  ```
12
12
 
13
- No server? Run `em watch {{model-name}}.em -o {{model-name}}.svg` and open
14
- `live.html?svg={{model-name}}.svg` in a browser (polls ~2s; share the screen).
13
+ Pan/zoom to navigate the diagram (drag, scroll; **Fit** resets), and click **Review mode** in
14
+ the header for a slice-by-slice walkthrough. If a save fails to render, the viewer keeps the
15
+ last good diagram and shows an error banner until the next successful load.
15
16
 
16
17
  Static render: `em render {{model-name}}.em -o {{model-name}}.svg`
17
18
 
@@ -32,11 +33,13 @@ never connected to one another.
32
33
 
33
34
  ## Slices
34
35
  <!-- The canonical slice index — the ONE place slices are enumerated (the state file
35
- points here rather than keeping its own copy). Keep it in sync as slice docs are
36
- written and as statuses move. -->
36
+ points here rather than keeping its own copy). Generated — run
37
+ `em slice index {{model-name}}.em` to (re)write the table below from the model and its
38
+ slice docs; never hand-edit between the markers. -->
39
+ <!-- GENERATED:slices:start -->
37
40
  | # | Slice | Pattern | Status | Implemented in | Design doc |
38
41
  |---|-------|---------|--------|----------------|------------|
39
- | 1 | {{Slice Name}} | {{Pattern}} | {{no doc yet / draft / reviewed / ready-to-implement / implemented}} | {{PR/commit link once implemented, else —}} | [slices/{{slice-name}}.md](slices/{{slice-name}}.md) |
42
+ <!-- GENERATED:slices:end -->
40
43
 
41
44
  ## Status
42
45
  See [`.event-modeling.md`](.event-modeling.md) for current phase, decisions, and open questions.
@@ -29,6 +29,12 @@ The three lineage keys only apply when this doc was produced by a split, merge,
29
29
  delete them otherwise; most slices never carry them. Grammar: `<slice-key>@v<N>`, where
30
30
  `<slice-key>` is the referenced slice's kebab-case filename stem.
31
31
 
32
+ `covers` only applies when this doc is also the ratified doc for a DIFFERENT slice — typically
33
+ the bare `view` half of the two-slice Automation/Translation shape, which has nothing of its own
34
+ to write up. Delete it otherwise; most docs never carry it. Requires a matching
35
+ `note "slices/{{this-slice-name}}.md"` on an element in the OTHER slice — this key alone doesn't
36
+ bind anything. Plain slice keys, comma-separated — not the `<slice-key>@v<N>` ref grammar above.
37
+
32
38
  Full machine schema — required-vs-optional keys per `status`, value types/enums, the
33
39
  unknown-key policy — is documented in docs/slice-doc-schema.md.
34
40
 
@@ -49,6 +55,10 @@ implementedIn: {{PR/commit link — fill in once status is `implemented`}}
49
55
  # split-from: <slice-key>@v<N>
50
56
  # merged-from: <slice-key>@v<N>, <slice-key>@v<N>
51
57
  # superseded-by: <slice-key>@v<N>, <slice-key>@v<N>
58
+ # Cross-slice coverage — only when this doc also covers another slice with no doc of its own
59
+ # (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.
61
+ # covers: <slice-key>, <slice-key>
52
62
  ---
53
63
  # Slice: {{Slice Name}}
54
64
 
@@ -68,7 +78,7 @@ implementedIn: {{PR/commit link — fill in once status is `implemented`}}
68
78
  **v{{X}} → v{{Y}}, ratified {{date}}** — {{one-line summary of the ratified change}}
69
79
 
70
80
  ### Added
71
- #### Requirement: {{title}} ({{stable ID, e.g. an INV-n from Invariants below}})
81
+ #### Requirement: {{title}} ({{stable ID, e.g. an INV-{{MNEMONIC}}-n from Invariants below}})
72
82
  {{requirement text — same voice as an Invariants entry}}
73
83
  ##### Scenario: {{name}}
74
84
  - **GIVEN** {{...}} **WHEN** {{...}} **THEN** {{...}}
@@ -132,15 +142,19 @@ an event directly.}}
132
142
  - **Freshness / consistency expectation:** {{real-time | eventual | on-demand}}
133
143
 
134
144
  ## Invariants / Business Rules
135
- <!-- What must ALWAYS hold. Give each a stable ID so tests and code can reference it. -->
136
- - **INV-1:** {{rule that the command enforces; violation ⇒ rejection}}
137
- - **INV-2:** {{...}}
145
+ <!-- What must ALWAYS hold. Give each a stable ID so tests and code can reference it:
146
+ `INV-<MNEMONIC>-<n>`, where `<MNEMONIC>` is a short (2-4 letter/digit), slice-unique
147
+ 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`). -->
150
+ - **INV-{{MNEMONIC}}-1:** {{rule that the command enforces; violation ⇒ rejection}}
151
+ - **INV-{{MNEMONIC}}-2:** {{...}}
138
152
 
139
153
  ## Scenarios (Given / When / Then)
140
154
  <!-- The executable specification. Cover the happy path AND the key rule boundaries. -->
141
155
  - **Happy path** — Given {{starting state / prior events}}, When {{command/trigger}},
142
156
  Then {{event(s) recorded}} and {{resulting read-model change}}.
143
- - **Rejected (INV-1)** — Given {{state}}, When {{command}}, Then {{rejected with reason}}; no event.
157
+ - **Rejected (INV-{{MNEMONIC}}-1)** — Given {{state}}, When {{command}}, Then {{rejected with reason}}; no event.
144
158
  - **{{Edge case}}** — Given {{...}}, When {{...}}, Then {{...}}.
145
159
 
146
160
  ## Alternate & Error Flows
package/README.md CHANGED
@@ -7,7 +7,9 @@ AI to write and edit as it is for a person, and `em validate` keeps either one h
7
7
 
8
8
  ![An order-fulfillment event model rendered by em](https://raw.githubusercontent.com/milehimikey/em/main/examples/order-fulfillment.svg)
9
9
 
10
- The source for that diagram is [about 70 lines of text](examples/order-fulfillment.em).
10
+ The source for that diagram is [about 70 lines of text](examples/order-fulfillment.em) — or
11
+ see it as a [full project](examples/order-fulfillment/), slice docs and all, for a guided
12
+ tour of the rest of the toolchain.
11
13
 
12
14
  ## Install
13
15
 
@@ -15,9 +17,9 @@ The source for that diagram is [about 70 lines of text](examples/order-fulfillme
15
17
  npm install -g @milehimikey/em
16
18
  ```
17
19
 
18
- Requires Node ≥ 18. SVG and PNG rendering are fully self-contained (Graphviz runs as
19
- bundled WebAssembly); nothing else to install. PDF is optional — see
20
- [docs/dependencies.md](docs/dependencies.md).
20
+ Requires Node ≥ 18. SVG, PNG, and PDF rendering are all fully self-contained (Graphviz runs
21
+ as bundled WebAssembly); nothing else to install. Only rarer formats (ps, eps, ...) need an
22
+ optional system dependency — see [docs/dependencies.md](docs/dependencies.md).
21
23
 
22
24
  ## Quickstart
23
25
 
@@ -1,7 +1,29 @@
1
1
  import { Slice } from "../model/model.js";
2
2
  import { Diagnostic } from "../model/validate.js";
3
3
  import { DriftSignalKind } from "./driftSignal.js";
4
- import { SliceRef } from "./sliceDoc.js";
4
+ import { SliceDoc, SliceRef } from "./sliceDoc.js";
5
+ export declare const NOTE_SLICE_PATH: RegExp;
6
+ /** One cross-binding candidate's resolution against `sliceKey`'s ratification requirement:
7
+ * the target file doesn't exist, exists but its frontmatter isn't usable, exists and is usable
8
+ * but doesn't list `sliceKey` in its `covers:` (doesn't carry the `doc` — nothing downstream
9
+ * needs it), or ratifies (does carry `doc`, the parse the caller needs next). */
10
+ export type CrossCandidateResolution = {
11
+ status: "missing";
12
+ } | {
13
+ status: "unusable";
14
+ } | {
15
+ status: "unratified";
16
+ } | {
17
+ status: "ratified";
18
+ doc: SliceDoc;
19
+ };
20
+ /**
21
+ * Resolves a single `slices/<candidateKey>.md` cross-binding candidate — the exact ratification
22
+ * predicate `resolveSliceDocJoin`'s cross-binding search (MIL-121) applies, extracted so
23
+ * `noteBindingValidate.ts` (MIL-126) can classify a *non*-winning candidate's reason without
24
+ * re-deriving what "ratifies" means. One source of truth for both.
25
+ */
26
+ export declare function resolveCrossCandidate(baseDir: string, candidateKey: string, sliceKey: string): CrossCandidateResolution;
5
27
  export type DocReason = "no-doc-bound" | "binding-missing-file" | "frontmatter-invalid" | null;
6
28
  /** A slice's export-facing doc join — always a non-null object (never JSON `null`), matching
7
29
  * every other optional field in `em export`'s style: explicit-null-on-a-stable-key, never a
@@ -9,8 +31,11 @@ export type DocReason = "no-doc-bound" | "binding-missing-file" | "frontmatter-i
9
31
  * cleanly; otherwise it names which of the three broken states applies. */
10
32
  export interface SliceDocExport {
11
33
  found: boolean;
12
- /** The conventional path (`slices/<sliceKey>.md`), always present regardless of `found` —
13
- * the path a doc would need to exist at, and (when `found`) the path it was read from. */
34
+ /** The bound doc's path — the conventional `slices/<sliceKey>.md` for the ordinary canonical
35
+ * binding, or (MIL-121) a different slice's canonical path when this slice's doc join
36
+ * resolved to a ratified cross-binding instead. Always present regardless of `found` — when
37
+ * nothing is bound (or bound-but-unratified), it's still the conventional path this slice's
38
+ * own doc would need to exist at. */
14
39
  path: string;
15
40
  reason: DocReason;
16
41
  status: string | null;