@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.
- package/.claude/skills/event-modeling/SKILL.md +92 -65
- package/.claude/skills/event-modeling/reference/conform.md +45 -20
- package/.claude/skills/event-modeling/reference/em-dsl.md +120 -55
- package/.claude/skills/event-modeling/reference/extract.md +5 -4
- package/.claude/skills/event-modeling/reference/implement.md +34 -22
- package/.claude/skills/event-modeling/templates/model-readme.md +8 -5
- package/.claude/skills/event-modeling/templates/slice.md +19 -5
- package/README.md +6 -4
- package/dist/catalog/docJoin.d.ts +28 -3
- package/dist/catalog/docJoin.js +82 -13
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/docModelConsistencyValidate.d.ts +8 -0
- package/dist/catalog/docModelConsistencyValidate.js +335 -0
- package/dist/catalog/docModelConsistencyValidate.js.map +1 -0
- package/dist/catalog/noteBindingValidate.d.ts +8 -0
- package/dist/catalog/noteBindingValidate.js +133 -0
- package/dist/catalog/noteBindingValidate.js.map +1 -0
- package/dist/catalog/pages.js +3 -3
- package/dist/catalog/pages.js.map +1 -1
- package/dist/catalog/sliceDoc.d.ts +14 -0
- package/dist/catalog/sliceDoc.js +22 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/catalog/sliceReadyValidate.d.ts +18 -0
- package/dist/catalog/sliceReadyValidate.js +25 -1
- package/dist/catalog/sliceReadyValidate.js.map +1 -1
- package/dist/cli/agentsMd.d.ts +15 -0
- package/dist/cli/agentsMd.js +72 -0
- package/dist/cli/agentsMd.js.map +1 -0
- package/dist/cli/conformScope.d.ts +86 -0
- package/dist/cli/conformScope.js +156 -0
- package/dist/cli/conformScope.js.map +1 -0
- package/dist/cli/contract.d.ts +7 -0
- package/dist/cli/contract.js +27 -0
- package/dist/cli/contract.js.map +1 -0
- package/dist/cli/coverage.d.ts +69 -0
- package/dist/cli/coverage.js +157 -0
- package/dist/cli/coverage.js.map +1 -0
- package/dist/cli/markImplemented.d.ts +34 -0
- package/dist/cli/markImplemented.js +180 -0
- package/dist/cli/markImplemented.js.map +1 -0
- package/dist/cli/migrateReactionShape.d.ts +62 -0
- package/dist/cli/migrateReactionShape.js +283 -0
- package/dist/cli/migrateReactionShape.js.map +1 -0
- package/dist/cli/sliceIndex.d.ts +49 -0
- package/dist/cli/sliceIndex.js +115 -0
- package/dist/cli/sliceIndex.js.map +1 -0
- package/dist/cli/sliceNew.d.ts +18 -0
- package/dist/cli/sliceNew.js +53 -0
- package/dist/cli/sliceNew.js.map +1 -0
- package/dist/cli/stateFile.d.ts +74 -0
- package/dist/cli/stateFile.js +191 -0
- package/dist/cli/stateFile.js.map +1 -0
- package/dist/cli.js +535 -43
- package/dist/cli.js.map +1 -1
- package/dist/emit/coverageJson.d.ts +8 -0
- package/dist/emit/coverageJson.js +30 -0
- package/dist/emit/coverageJson.js.map +1 -0
- package/dist/emit/dot.js +41 -10
- package/dist/emit/dot.js.map +1 -1
- package/dist/emit/json.d.ts +72 -4
- package/dist/emit/json.js +73 -4
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/validateJson.d.ts +52 -0
- package/dist/emit/validateJson.js +120 -0
- package/dist/emit/validateJson.js.map +1 -0
- package/dist/mcp/main.d.ts +2 -0
- package/dist/mcp/main.js +20 -0
- package/dist/mcp/main.js.map +1 -0
- package/dist/mcp/server.d.ts +11 -0
- package/dist/mcp/server.js +260 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/model/model.d.ts +30 -1
- package/dist/model/model.js +26 -0
- package/dist/model/model.js.map +1 -1
- package/dist/model/rules.d.ts +114 -0
- package/dist/model/rules.js +116 -4
- package/dist/model/rules.js.map +1 -1
- package/dist/model/validate.js +83 -7
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +41 -0
- package/dist/parser/lexer.d.ts +16 -0
- package/dist/parser/lexer.js +44 -2
- package/dist/parser/lexer.js.map +1 -1
- package/dist/parser/parser.js +340 -20
- package/dist/parser/parser.js.map +1 -1
- package/dist/render/drawNotes.js +8 -1
- package/dist/render/drawNotes.js.map +1 -1
- package/dist/render/render.js +85 -17
- package/dist/render/render.js.map +1 -1
- package/dist/render/serve.d.ts +4 -0
- package/dist/render/serve.js +23 -227
- package/dist/render/serve.js.map +1 -1
- package/dist/render/sliceStatus.d.ts +1 -1
- package/dist/render/sliceStatus.js +35 -6
- package/dist/render/sliceStatus.js.map +1 -1
- package/dist/render/viewerHtml.d.ts +1 -0
- package/dist/render/viewerHtml.js +5 -0
- package/dist/render/viewerHtml.js.map +1 -0
- package/dist/render/watch.d.ts +10 -0
- package/dist/render/watch.js +29 -0
- package/dist/render/watch.js.map +1 -1
- package/dist/templates.d.ts +19 -0
- package/dist/templates.js +169 -0
- package/dist/templates.js.map +1 -1
- package/dist/util/markers.d.ts +13 -0
- package/dist/util/markers.js +30 -0
- package/dist/util/markers.js.map +1 -0
- package/dist/util/walkDir.d.ts +10 -1
- package/dist/util/walkDir.js +11 -7
- package/dist/util/walkDir.js.map +1 -1
- package/package.json +12 -3
- 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
|
|
16
|
-
em init [file]
|
|
17
|
-
em init [file] -f, --force
|
|
18
|
-
em
|
|
19
|
-
em
|
|
20
|
-
em render <file>
|
|
21
|
-
em render <file> --
|
|
22
|
-
em render <file> --
|
|
23
|
-
em render <file> --
|
|
24
|
-
em
|
|
25
|
-
em
|
|
26
|
-
em
|
|
27
|
-
em
|
|
28
|
-
em
|
|
29
|
-
em diff <old> [new]
|
|
30
|
-
em diff <old> [new] --
|
|
31
|
-
em
|
|
32
|
-
em
|
|
33
|
-
em
|
|
34
|
-
em glossary <files>
|
|
35
|
-
em glossary <files> --
|
|
36
|
-
em
|
|
37
|
-
em
|
|
38
|
-
em
|
|
39
|
-
em catalog <files>
|
|
40
|
-
em catalog <files>
|
|
41
|
-
em
|
|
42
|
-
em
|
|
43
|
-
em
|
|
44
|
-
em
|
|
45
|
-
em
|
|
46
|
-
em
|
|
47
|
-
em
|
|
48
|
-
em
|
|
49
|
-
em
|
|
50
|
-
em
|
|
51
|
-
em
|
|
52
|
-
em
|
|
53
|
-
em
|
|
54
|
-
em
|
|
55
|
-
em
|
|
56
|
-
em
|
|
57
|
-
em
|
|
58
|
-
em
|
|
59
|
-
em
|
|
60
|
-
em
|
|
61
|
-
em
|
|
62
|
-
em
|
|
63
|
-
em
|
|
64
|
-
em
|
|
65
|
-
em
|
|
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
|
|
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)
|
|
27
|
-
|
|
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
|
-
|
|
130
|
-
docs exist until the `slice` phase
|
|
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`).
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
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
|
|
55
|
-
DSL or the doc's frontmatter yourself: it
|
|
56
|
-
and the joined doc metadata (`slice.doc.status`,
|
|
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
|
|
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.
|
|
116
|
-
|
|
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
|
|
119
|
-
slice table reflects the new status and
|
|
120
|
-
|
|
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,
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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).
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|

|
|
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
|
|
19
|
-
bundled WebAssembly); nothing else to install.
|
|
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
|
|
13
|
-
*
|
|
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;
|