@milehimikey/em 1.20.0 → 1.22.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/agents/em-implementer.md +10 -0
- package/.claude/agents/em-validator.md +1 -0
- package/.claude/skills/event-modeling/SKILL.md +1 -1
- package/.claude/skills/event-modeling-conform/SKILL.md +7 -4
- package/.claude/skills/event-modeling-conform/reference/conform.md +82 -4
- package/.claude/skills/event-modeling-design/SKILL.md +23 -8
- package/.claude/skills/event-modeling-discover/SKILL.md +2 -2
- package/.claude/skills/event-modeling-discover/reference/extract.md +5 -1
- package/.claude/skills/event-modeling-engagement/SKILL.md +1 -1
- package/.claude/skills/event-modeling-engagement/reference/engagement.md +1 -0
- package/.claude/skills/event-modeling-implement/SKILL.md +5 -3
- package/.claude/skills/event-modeling-implement/reference/implement.md +14 -0
- package/.claude/skills/event-modeling-review/SKILL.md +19 -7
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +54 -9
- package/.claude/skills/event-modeling-shared/reference/methodology.md +35 -2
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +26 -15
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +2 -1
- package/.claude/skills/event-modeling-shared/templates/model-readme.md +10 -2
- package/.claude/skills/event-modeling-shared/templates/slice.md +7 -1
- package/.claude-plugin/marketplace.json +2 -2
- package/dist/catalog/build.js +6 -5
- package/dist/catalog/build.js.map +1 -1
- package/dist/catalog/coverage.d.ts +3 -0
- package/dist/catalog/coverage.js +12 -0
- package/dist/catalog/coverage.js.map +1 -1
- package/dist/catalog/docLocate.d.ts +17 -0
- package/dist/catalog/docLocate.js +97 -0
- package/dist/catalog/docLocate.js.map +1 -0
- package/dist/catalog/frontmatterCoherenceValidate.js +16 -1
- package/dist/catalog/frontmatterCoherenceValidate.js.map +1 -1
- package/dist/catalog/orphanedSliceDocValidate.js +15 -10
- package/dist/catalog/orphanedSliceDocValidate.js.map +1 -1
- package/dist/catalog/readSliceDoc.js +5 -4
- package/dist/catalog/readSliceDoc.js.map +1 -1
- package/dist/catalog/scenarioQualityValidate.js +2 -2
- package/dist/catalog/scenarioQualityValidate.js.map +1 -1
- package/dist/catalog/sliceDoc.d.ts +8 -0
- package/dist/catalog/sliceDoc.js +9 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/catalog/sliceSections.d.ts +18 -1
- package/dist/catalog/sliceSections.js +38 -4
- package/dist/catalog/sliceSections.js.map +1 -1
- package/dist/cli/api.d.ts +84 -0
- package/dist/cli/api.js +146 -15
- package/dist/cli/api.js.map +1 -1
- package/dist/cli/bindings.d.ts +152 -0
- package/dist/cli/bindings.js +462 -0
- package/dist/cli/bindings.js.map +1 -0
- package/dist/cli/conformScope.d.ts +32 -0
- package/dist/cli/conformScope.js +68 -5
- package/dist/cli/conformScope.js.map +1 -1
- package/dist/cli/defer.js +3 -2
- package/dist/cli/defer.js.map +1 -1
- package/dist/cli/engagement.d.ts +24 -1
- package/dist/cli/engagement.js +50 -6
- package/dist/cli/engagement.js.map +1 -1
- package/dist/cli/engagementBrief.d.ts +127 -0
- package/dist/cli/engagementBrief.js +313 -0
- package/dist/cli/engagementBrief.js.map +1 -0
- package/dist/cli/engagementFile.d.ts +14 -0
- package/dist/cli/engagementFile.js +54 -0
- package/dist/cli/engagementFile.js.map +1 -1
- package/dist/cli/ledgerCheck.js +11 -8
- package/dist/cli/ledgerCheck.js.map +1 -1
- package/dist/cli/markImplemented.js +2 -2
- package/dist/cli/markImplemented.js.map +1 -1
- package/dist/cli/ratify.js +2 -2
- package/dist/cli/ratify.js.map +1 -1
- package/dist/cli/refsCheck.js +3 -2
- package/dist/cli/refsCheck.js.map +1 -1
- package/dist/cli/render-inputs.d.ts +1 -1
- package/dist/cli/render-inputs.js +3 -2
- package/dist/cli/render-inputs.js.map +1 -1
- package/dist/cli/review.js +2 -2
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/revise.js +2 -2
- package/dist/cli/revise.js.map +1 -1
- package/dist/cli/skillDirs.d.ts +22 -0
- package/dist/cli/skillDirs.js +24 -0
- package/dist/cli/skillDirs.js.map +1 -1
- package/dist/cli/sliceConform.js +2 -1
- package/dist/cli/sliceConform.js.map +1 -1
- package/dist/cli/sliceIndex.d.ts +19 -3
- package/dist/cli/sliceIndex.js +81 -17
- package/dist/cli/sliceIndex.js.map +1 -1
- package/dist/cli/sliceNew.js +2 -0
- package/dist/cli/sliceNew.js.map +1 -1
- package/dist/cli/sliceStubAll.js +7 -2
- package/dist/cli/sliceStubAll.js.map +1 -1
- package/dist/cli/sliceSync.d.ts +12 -0
- package/dist/cli/sliceSync.js +42 -4
- package/dist/cli/sliceSync.js.map +1 -1
- package/dist/cli/status.d.ts +8 -4
- package/dist/cli/status.js +6 -4
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/storylineSplit.d.ts +18 -0
- package/dist/cli/storylineSplit.js +110 -0
- package/dist/cli/storylineSplit.js.map +1 -0
- package/dist/cli/upgrade.js +47 -23
- package/dist/cli/upgrade.js.map +1 -1
- package/dist/cli/watchStoryline.d.ts +6 -0
- package/dist/cli/watchStoryline.js +10 -0
- package/dist/cli/watchStoryline.js.map +1 -1
- package/dist/cli.js +408 -78
- package/dist/cli.js.map +1 -1
- package/dist/emit/api.d.ts +8 -1
- package/dist/emit/api.js +68 -24
- package/dist/emit/api.js.map +1 -1
- package/dist/emit/apiSurfaceJson.d.ts +3 -0
- package/dist/emit/apiSurfaceJson.js +26 -0
- package/dist/emit/apiSurfaceJson.js.map +1 -0
- package/dist/emit/engagementJson.d.ts +8 -3
- package/dist/emit/engagementJson.js +31 -4
- package/dist/emit/engagementJson.js.map +1 -1
- package/dist/emit/json.d.ts +22 -1
- package/dist/emit/json.js +12 -1
- package/dist/emit/json.js.map +1 -1
- package/dist/mcp/server.js +111 -9
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/edges.d.ts +1 -1
- package/dist/model/edges.js +8 -1
- package/dist/model/edges.js.map +1 -1
- package/dist/model/loadModel.d.ts +38 -0
- package/dist/model/loadModel.js +122 -0
- package/dist/model/loadModel.js.map +1 -0
- package/dist/model/model.d.ts +26 -9
- package/dist/model/model.js +22 -17
- package/dist/model/model.js.map +1 -1
- package/dist/model/rules.d.ts +23 -0
- package/dist/model/rules.js +23 -0
- package/dist/model/rules.js.map +1 -1
- package/dist/model/shapeLint.js +95 -0
- package/dist/model/shapeLint.js.map +1 -1
- package/dist/model/sliceDocIndex.js +13 -5
- package/dist/model/sliceDocIndex.js.map +1 -1
- package/dist/model/sliceDocPath.d.ts +19 -0
- package/dist/model/sliceDocPath.js +49 -0
- package/dist/model/sliceDocPath.js.map +1 -0
- package/dist/model/storylineMap.d.ts +20 -0
- package/dist/model/storylineMap.js +55 -0
- package/dist/model/storylineMap.js.map +1 -0
- package/dist/model/validate.d.ts +7 -0
- package/dist/model/validate.js +8 -0
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +19 -0
- package/dist/parser/parser.d.ts +17 -1
- package/dist/parser/parser.js +91 -2
- package/dist/parser/parser.js.map +1 -1
- package/dist/pipeline.d.ts +16 -0
- package/dist/pipeline.js +19 -2
- package/dist/pipeline.js.map +1 -1
- package/dist/render/drawEdges.d.ts +5 -1
- package/dist/render/drawEdges.js +57 -4
- package/dist/render/drawEdges.js.map +1 -1
- package/dist/render/drawNotes.d.ts +4 -1
- package/dist/render/drawNotes.js +40 -10
- package/dist/render/drawNotes.js.map +1 -1
- package/dist/render/render.d.ts +11 -0
- package/dist/render/render.js +94 -11
- package/dist/render/render.js.map +1 -1
- package/dist/render/serve.d.ts +5 -0
- package/dist/render/serve.js +18 -2
- package/dist/render/serve.js.map +1 -1
- package/dist/render/sliceDiagram.js +3 -0
- package/dist/render/sliceDiagram.js.map +1 -1
- package/dist/render/sliceOverlay.d.ts +4 -5
- package/dist/render/sliceOverlay.js +4 -7
- package/dist/render/sliceOverlay.js.map +1 -1
- package/dist/render/sliceStatus.d.ts +8 -0
- package/dist/render/sliceStatus.js +22 -15
- package/dist/render/sliceStatus.js.map +1 -1
- package/dist/render/storylineBands.d.ts +0 -3
- package/dist/render/storylineBands.js +2 -22
- package/dist/render/storylineBands.js.map +1 -1
- package/dist/render/storylineDiagram.js +17 -69
- package/dist/render/storylineDiagram.js.map +1 -1
- package/dist/render/storylineMap.d.ts +18 -0
- package/dist/render/storylineMap.js +263 -0
- package/dist/render/storylineMap.js.map +1 -0
- package/dist/render/svgGeometry.d.ts +9 -0
- package/dist/render/svgGeometry.js +16 -0
- package/dist/render/svgGeometry.js.map +1 -1
- package/dist/render/viewerHtml.js +1 -1
- package/dist/render/viewerHtml.js.map +1 -1
- package/dist/templates.d.ts +5 -1
- package/dist/templates.js +39 -2
- package/dist/templates.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/agents/em-implementer.md +10 -0
- package/plugin/agents/em-validator.md +1 -0
- package/plugin/shared/reference/em-dsl.md +54 -9
- package/plugin/shared/reference/methodology.md +35 -2
- package/plugin/shared/reference/operating-principles.md +26 -15
- package/plugin/shared/reference/slice-doc-schema.md +2 -1
- package/plugin/shared/templates/model-readme.md +10 -2
- package/plugin/shared/templates/slice.md +7 -1
- package/plugin/skills/conform/SKILL.md +7 -4
- package/plugin/skills/conform/reference/conform.md +82 -4
- package/plugin/skills/design/SKILL.md +23 -8
- package/plugin/skills/discover/SKILL.md +2 -2
- package/plugin/skills/discover/reference/extract.md +5 -1
- package/plugin/skills/engagement/SKILL.md +1 -1
- package/plugin/skills/engagement/reference/engagement.md +1 -0
- package/plugin/skills/event-modeling/SKILL.md +1 -1
- package/plugin/skills/implement/SKILL.md +5 -3
- package/plugin/skills/implement/reference/implement.md +14 -0
- package/plugin/skills/review/SKILL.md +19 -7
|
@@ -16,14 +16,15 @@ Keep `.em` files focused on **structure**; put deep design in markdown linked vi
|
|
|
16
16
|
em --version # print the installed em version
|
|
17
17
|
em init [file] # scaffold a starter .em model
|
|
18
18
|
em init [file] -f, --force # overwrite if the file exists
|
|
19
|
-
em scaffold <name> # scaffold a full project: <slug>/<slug>.em, README.md, .event-modeling.md, and constitution.md (the implementation constitution — skipped, with a note, in a spec-kit project, where `.specify/memory/constitution.md` IS that document) (see docs/cli.md — for just a starter .em, use `em init`; for a multi-model project, pass --under to nest it under a shared parent directory)
|
|
19
|
+
em scaffold <name> # scaffold a full project: <slug>/<slug>.em, README.md, .event-modeling.md, and constitution.md (the implementation constitution — skipped, with a note, in a spec-kit project, where `.specify/memory/constitution.md` IS that document), plus the standard slices/, contracts/, model-versions/ and conformance/bindings/ directories (each seeded with a one-line README naming what lands there and which command writes it, instead of a .gitkeep) (see docs/cli.md — for just a starter .em, use `em init`; for a multi-model project, pass --under to nest it under a shared parent directory)
|
|
20
20
|
em scaffold <name> -f, --force # overwrite the directory's contents if it already exists
|
|
21
21
|
em scaffold <name> --under <dir> # parent directory to scaffold into — writes <dir>/<slug>/ instead of ./<slug>/, the supported multi-model layout (docs/cli.md, "Multi-model projects"): one directory per model, so each model's slices/ never collides with a sibling model's
|
|
22
22
|
em render <file> # transpile a model and render it (or emit DOT)
|
|
23
23
|
em render <file> -o, --out <path> # output path (extension picks the format)
|
|
24
24
|
em render <file> -T, --format <fmt> # output format (svg, png, pdf, ...)
|
|
25
25
|
em render <file> --slice <name> # render only this slice, redrawn in its own canonical pattern shape (default out: slices/<kebab-slug>.svg)
|
|
26
|
-
em render <file> --storyline <name> # render only this storyline: its slices in order,
|
|
26
|
+
em render <file> --storyline <name> # render only this storyline: its own slices in order, nothing from outside it (default out: storylines/<kebab-slug>.svg)
|
|
27
|
+
em render <file> --map # render every storyline to storylines/<kebab-slug>.svg plus a draggable canvas of them all, with the flow between storylines, at storylines/map.html (-o picks the html path)
|
|
27
28
|
em render <file> --emit-dot # print the generated DOT instead of rendering
|
|
28
29
|
em render <file> --keep-empty-lanes # keep the API lane even when empty
|
|
29
30
|
em export <file> # export a versioned JSON snapshot of the normalized model
|
|
@@ -37,6 +38,8 @@ em api generate <file> --stdout # print the
|
|
|
37
38
|
em api check <file> # is the committed contract current? With --base, annotate each public-surface change since that revision additive or breaking (annotation only — exits 1 only when stale)
|
|
38
39
|
em api check <file> --base <rev> # git revision to diff the public surface against
|
|
39
40
|
em api check <file> --json # print a JSON document instead of the text report (see docs/cli.md)
|
|
41
|
+
em api surface <file> # the model's public commands, events, views and reachable declared types as a versioned JSON document for companion tools (GH #448, see docs/cli.md)
|
|
42
|
+
em api surface <file> --json # print a JSON document instead of the text listing (see docs/cli.md)
|
|
40
43
|
em typespec <file> # DEPRECATED alias of `em api generate --stdout` (use em api generate)
|
|
41
44
|
em typespec <file> -o, --out <path> # write to a file instead of stdout
|
|
42
45
|
em diff <old> [new] # compare two models structurally (two files, or one file across git revisions)
|
|
@@ -71,6 +74,7 @@ em slice stub-all <file> --implemented-in <url> # PR/commit
|
|
|
71
74
|
em slice stub-all <file> --dry-run # list what would be stubbed/wired without writing anything
|
|
72
75
|
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
|
|
73
76
|
em slice index <file> --check # verify the table is current; exit non-zero on drift without writing (CI)
|
|
77
|
+
em slice index <file> --full # every lifecycle column (reviewed/ratified/owner/tracking/implemented/conformed) instead of the reader's five
|
|
74
78
|
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). Applies only to a `ready-to-implement` doc (MIL-277: a `draft`/`reviewed` doc refuses — ratify first). Also records the shipped version beside the link (MIL-284): `shippedVersion:`, `shippedRef:` (the doc's `ratifiedRef:`, when it has one) and `shippedOn:`, and opens the next version as a `draft` when an Open Question was deferred to it (MIL-283). Idempotent on the same URL — also after that draft opened; refuses to overwrite a different one; never touches the doc body except to re-open those deferred questions
|
|
75
79
|
em slice mark-implemented <file> <slice-key> <pr-url> --on <date> # merge date recorded as `shippedOn:`, YYYY-MM-DD (default: today, local date; MIL-284)
|
|
76
80
|
em slice mark-implemented <file> <slice-key> <pr-url> --json # print a JSON document instead of text: { path, changed, implementedIn, nextDraft: { version, reopened[] } | null } (GH #285)
|
|
@@ -141,6 +145,11 @@ em conform-supersede <file> <report-path> --locus <locus> # who/what t
|
|
|
141
145
|
em conform-supersede <file> <report-path> --by <name> # who ruled on the named finding(s) (requires --locus)
|
|
142
146
|
em conform-findings check <path> # shape-validate a conformance/<date>-findings.json file (findingsSchemaVersion, required fields, enum values, ids sorted+unique, a ruled finding carries resolvedBy+resolvedOn) — exit 1 on shape errors. The conform skill writes this file directly alongside its report; this is how a headless run verifies what it just wrote.
|
|
143
147
|
em conform-findings check <path> --json # print a JSON document instead of the text report
|
|
148
|
+
em conform bindings check <file> # verify every per-slice bindings manifest (conformance/bindings/<slice-key>.yaml, see docs/bindings.md): each bound element's `ref` resolves against the compiled model, and each bound `file` exists under --repo and contains its `identifier` as a plain substring (grep-level only, never code-parsed — language-agnostic). Distinct failure kinds: an unknown `--slice` key, a missing bindings file, malformed YAML, an unknown slice named inside a file, and an unknown element ref. Advisory only — exit code stays 0 unless --exit-code is given and a binding failed (the opt-in CI gate, same posture as `em refs --reachable-from`).
|
|
149
|
+
em conform bindings check <file> --repo <path> # path to the target codebase (bound `file`s resolve relative to it; never required to be a git repository)
|
|
150
|
+
em conform bindings check <file> --slice <key> # check only this slice's bindings file (default: every conformance/bindings/*.yaml)
|
|
151
|
+
em conform bindings check <file> --json # print a JSON document instead of the text report
|
|
152
|
+
em conform bindings check <file> --exit-code # exit 1 if any binding failed (git-diff convention) — the opt-in CI gate
|
|
144
153
|
em discover triage-check <file> # validate a discovery/prd-triage.json (prdTriageSchemaVersion 1.0): shape, the shipped rubric version, every statement/storyline quote appearing verbatim in the input document, and every statement carrying a category, an outcome and (modeled/rewritten) a group + storyline — exit 1 on errors. The discover skill writes this file directly; this is how a headless run verifies it.
|
|
145
154
|
em discover triage-check <file> --source <path> # the input PRD/spec to check quotes against (default: the file's own `source`, relative to it)
|
|
146
155
|
em discover triage-check <file> --markdown # after a passing check, print the Markdown view of the record instead of the summary
|
|
@@ -241,17 +250,23 @@ em engagement new <file> <slug> --parallel <n> # how many s
|
|
|
241
250
|
em engagement new <file> <slug> --by <name> # who created the engagement (createdBy)
|
|
242
251
|
em engagement new <file> <slug> --tracking <key> # an issue-tracker key or URL the slice PRs cite (a slice doc's own tracking: wins)
|
|
243
252
|
em engagement new <file> <slug> --pr-title <template> # PR title template for the slice PRs; {key} = tracking key, {slice} = slice name, {sliceKey} = slice key (default "{key}: {slice}", or "{slice}" without a key)
|
|
253
|
+
em engagement new <file> <slug> --context-doc <path> # repo-relative path to a document implementers need beyond the slice doc — the constitution, an NFR doc, an ADR (repeatable; GH #455)
|
|
244
254
|
em engagement new <file> <slug> --force # overwrite an existing engagement file with this slug
|
|
245
255
|
em engagement plan <file> <slug> # the engagement's build plan: dependency levels over model.edges (loops-to excluded), and per slice its readiness, branch impl/<key>, base (always main — MIL-280), and any hold
|
|
246
256
|
em engagement plan <file> <slug> --json # print a JSON document instead of text (same document as the MCP engagement_plan tool)
|
|
247
257
|
em engagement foundation <file> <slug> # GH #240: is the foundation PR (implement.md §8) actually in the code for the events this engagement's slices emit and consume? A text scan of --code: an event is present when a file carries an identifier derived from its name (OrderPlaced / order_placed / ORDER_PLACED / order-placed), its model fields and tag fields are then looked for in those files. Per event: present | incomplete (missing-field / missing-tag, named) | missing-event — so "foundation absent" and "foundation incomplete" are different reports. Exits 1 unless every event is present. Proves the contract was written down, not that it type-checks
|
|
248
258
|
em engagement foundation <file> <slug> --code <dir> # the target repository (or the directory the constitution's "where a slice's code lives" rule points at)
|
|
249
259
|
em engagement foundation <file> <slug> --json # print a JSON document instead of text (same document as the MCP engagement_foundation tool)
|
|
250
|
-
em engagement
|
|
251
|
-
em engagement
|
|
252
|
-
em engagement
|
|
253
|
-
em engagement
|
|
254
|
-
em engagement set <file> <slug>
|
|
260
|
+
em engagement brief <file> <slug> # GH #454: a deterministic, self-contained per-slice work brief for implementers em does not control (a ticket-driven cloud agent) — the bound slice doc verbatim (or the model's own elements when there is none), model context from the edge list, the public-contract surface, a definition-of-done (bindings manifest skeleton + conform check + isolation + PR title), and the engagement's context: documents. One brief per engagement slice unless --slice narrows it
|
|
261
|
+
em engagement brief <file> <slug> --slice <key> # only this slice's brief (export key, display name, or unique prefix)
|
|
262
|
+
em engagement brief <file> <slug> --inline-context # embed each context document's full text instead of just its path
|
|
263
|
+
em engagement brief <file> <slug> --json # print a JSON document instead of text (same document as the MCP engagement_brief tool)
|
|
264
|
+
em engagement set <file> <slug> [key] # the only write path to the Ledger: set one slice's state (and optionally branch/base/pr); idempotent; merged is terminal; --state held records a human hold. Or, on its own (no slice key), --context-doc replaces the engagement-level context: list (GH #455)
|
|
265
|
+
em engagement set <file> <slug> [key] --state <state> # one of: planned | building | validating | review | awaiting-merge | merged | held | gap
|
|
266
|
+
em engagement set <file> <slug> [key] --branch <name> # the slice's branch
|
|
267
|
+
em engagement set <file> <slug> [key] --base <name> # the branch the slice's PR targets
|
|
268
|
+
em engagement set <file> <slug> [key] --pr <url> # the slice's PR URL
|
|
269
|
+
em engagement set <file> <slug> [key] --context-doc <path> # repo-relative path to a document implementers need beyond the slice doc (repeatable; replaces the whole engagement-level context: list; GH #455)
|
|
255
270
|
em engagement status <file> <slug> # the Ledger joined with each slice doc's current status, plus whether the engagement is closable
|
|
256
271
|
em engagement status <file> <slug> --json # print a JSON document instead of text (same document as the MCP engagement_status tool)
|
|
257
272
|
em engagement close <file> <slug> # set status: closed — refuses unless every slice is merged (recorded or inferred) or gap
|
|
@@ -276,15 +291,25 @@ em mcp # start an M
|
|
|
276
291
|
em skill install # copy the event-modeling skill bundle into .claude/skills/ (event-modeling, event-modeling-discover/-design/-implement/-conform/-review, event-modeling-shared); across a structural bundle change (e.g. the MIL-157 split of the old single event-modeling/ directory into this six-directory bundle), `em skill sync` is the migration path — --force now performs the same reconcile (MIL-180)
|
|
277
292
|
em skill install -f, --force # overwrite an existing installation, reconciling added/updated/removed files within the bundle's own managed directories the same way `em skill sync` does — never touching an unrelated sibling skill (MIL-180)
|
|
278
293
|
em skill install --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
|
|
294
|
+
em skill install --skills-dir <dir> # where the vendored skill bundle lives, relative to the repo root (default .claude/skills; .agents/skills when that exists and .claude/skills does not — GH #412)
|
|
295
|
+
em skill install --agents-dir <dir> # where the four em sub-agent definitions live, relative to the repo root (default .claude/agents, or .agents/agents beside an .agents/skills bundle — GH #412)
|
|
279
296
|
em skill sync [path] # update the vendored .claude/skills/ event-modeling skill bundle in [path] to match the installed em package (overwrites unconditionally; local edits are never merged, MIL-93)
|
|
280
297
|
em skill sync [path] --no-agents-md # skip writing/updating the AGENTS.md agent-contract section (on by default, MIL-129)
|
|
298
|
+
em skill sync [path] --skills-dir <dir> # where the vendored skill bundle lives, relative to the repo root (default .claude/skills; .agents/skills when that exists and .claude/skills does not — GH #412)
|
|
299
|
+
em skill sync [path] --agents-dir <dir> # where the four em sub-agent definitions live, relative to the repo root (default .claude/agents, or .agents/agents beside an .agents/skills bundle — GH #412)
|
|
281
300
|
em skill check [path] # check [path]'s em skills for drift against the installed em package: the em Claude Code plugin's pin (settings + this machine's marketplace registry) when the repo declares it, and the vendored .claude/skills/ bundle when present; exits non-zero on any mismatch (CI-ready, MIL-93, MIL-231)
|
|
282
301
|
em skill check [path] --json # print a JSON document instead of the text report (see docs/cli.md)
|
|
283
302
|
em skill check [path] --ci # also fail (exit 1) when the declared plugin is not registered on this machine (implied by CI=true in the environment); without it that finding prints the install commands on stderr and exits 0
|
|
303
|
+
em skill check [path] --skills-dir <dir> # where the vendored skill bundle lives, relative to the repo root (default .claude/skills; .agents/skills when that exists and .claude/skills does not — GH #412)
|
|
304
|
+
em skill check [path] --agents-dir <dir> # where the four em sub-agent definitions live, relative to the repo root (default .claude/agents, or .agents/agents beside an .agents/skills bundle — GH #412)
|
|
284
305
|
em ci init <model> # install .github/workflows/em-ci.yml (PR gates: em validate, em slice index --check, em coverage --strict, em ledger, em skill check, em glossary --fail-on-conflicts, plus a push-triggered status-badge rebuild) and em-conform.yml (scheduled, advisory-only conformance cadence) — same install discipline as `em skill install`: marker-delimited, idempotent, --check for CI self-verification (MIL-166, see docs/ci.md). Give it a system.yaml (or its directory) instead of a model to cover every model in the system: one workflow, per-model jobs, a conform matrix (MIL-233)
|
|
285
306
|
em ci init <model> --tests <dir> # test directory the coverage/status steps scan for INV-* citations
|
|
286
307
|
em ci init <model> -f, --force # replace an existing workflow file that has no GENERATED markers, or a managed block generated for a different set of models
|
|
287
308
|
em ci init <model> --check # verify both files match the current preset; exit non-zero on drift without writing (CI)
|
|
309
|
+
em storyline split <file> # move each inline `storyline "X" { … }` block into its own storyline model file <slug>/<slug>.em (a `storyline "X" [source]` header, then the block's slices) and leave the one-line declaration `storyline "X" [after "Y"]` in the root (GH #421). Slice docs stay under the root's slices/. A storyline declared in several blocks is left alone (merge them first)
|
|
310
|
+
em storyline split <file> --storyline <name> # split only this storyline
|
|
311
|
+
em storyline split <file> --dry-run # print what would be written without touching anything
|
|
312
|
+
em storyline split <file> -f, --force # overwrite a storyline model file that already exists
|
|
288
313
|
em storyline suggest <file> # advisory (GH #290): propose `storyline` blocks for a model that has none — one per distinct slice `source "url"`, the remaining slices grouped by the view-instance chains (`again`) they feed — printed as a unified diff on stdout (apply with `patch -p1` or `git apply`); never writes the model
|
|
289
314
|
em upgrade <file> # bring a model repo authored under an older em (1.6 forward) up to the installed version: an ordered list of mechanical steps (skill bundle, reaction shape, state-file bullets, generated CI blocks, constitution scaffold) plus a human list of things no command can safely decide by itself (MIL-219, see docs/upgrading.md). Dry-run by default; --apply makes one git commit per applicable step, refusing on a dirty working tree
|
|
290
315
|
em upgrade <file> --apply # apply every applicable mechanical step, one git commit each, then a final `Em version:` commit
|
|
@@ -311,12 +336,14 @@ context Name # an event swimlane row (bounded context / aggr
|
|
|
311
336
|
storyline "Name" [source "url"] [after "Other"] { # optional chapter grouping slices (see Storylines)
|
|
312
337
|
slice "Name" { ... }
|
|
313
338
|
}
|
|
339
|
+
storyline "Name" [after "Other"] [in "path.em"] # a storyline MODEL: its slices live in <slug>/<slug>.em (see Storylines)
|
|
314
340
|
|
|
315
341
|
slice "Name" [source "url"] { # one vertical time step (a column); source is optional
|
|
316
342
|
ui Free Text @Persona # screen; @Persona picks its row (defaults to first/"User")
|
|
317
343
|
command Free Text # state-changing request (API band)
|
|
318
344
|
invariant INV-ORD-1 "Rule" # rule guarding the command/event just above (see Clauses)
|
|
319
345
|
view Free Text from "Event A", "Event B" # read model fed by event(s)
|
|
346
|
+
view Free Text from "Event A" updates from "Event B", "Event C" # B, C keep rows current: read, never drawn (see Clauses)
|
|
320
347
|
view Free Text again from "Event C" # later instance of an evolving read model (see Clauses)
|
|
321
348
|
event Free Text @Context # recorded fact; @Context picks its row (defaults to "Domain")
|
|
322
349
|
processor Free Text from "View" # automation; aliases: automation | saga | translation
|
|
@@ -360,6 +387,14 @@ type Name { field: Type, ... } # named structured type, reusable from any f
|
|
|
360
387
|
previous instance of V that also reads E), and EVERY site needs a reader at or after it (add `view V again from "E"`
|
|
361
388
|
after the last site, or `event-unread` warns for that site). The same name in a different context
|
|
362
389
|
is still a `duplicate-name` warning.
|
|
390
|
+
- **`updates from "Event", …`** (views only, GH #414): the subset of a view's sources that only keep a
|
|
391
|
+
row current or remove it (a due date moved, an address changed, a loan returned). They are folded
|
|
392
|
+
into the view's `from` for every check — `derived from`, completeness, the generated built-from
|
|
393
|
+
list — but never drawn as arrows: the diagram shows the line from the event that puts work on the
|
|
394
|
+
list, and lists the rest under "Kept current by". The author chooses the triggers: `view Holds To
|
|
395
|
+
Fulfill from "Hold Placed", "Tool Returned" updates from "Hold Collected", "Hold Cancelled"`.
|
|
396
|
+
Valid on an `again` instance too. A to-do view with more than one or two inbound arrows is missing
|
|
397
|
+
this split.
|
|
363
398
|
- **`again`** (views only): `view <Name> again [from "Event", …]` declares a later instance of an
|
|
364
399
|
already-declared read model — the forward-only device for a view that evolves as later events
|
|
365
400
|
land. Instances are ONE logical view: the first declaration owns the `note` binding; each
|
|
@@ -550,8 +585,15 @@ type Name { field: Type, ... } # named structured type, reusable from any f
|
|
|
550
585
|
Slices keep their file-order timeline position; a slice outside any storyline is allowed;
|
|
551
586
|
storylines don't nest and a repeated name continues that storyline (its slices join it; a continuation may not add or change
|
|
552
587
|
`source`/`after` — parse error). The full render draws a
|
|
553
|
-
labelled band above the slice headers; `em render --storyline "Name"` draws one story alone
|
|
554
|
-
|
|
588
|
+
labelled band above the slice headers; `em render --storyline "Name"` draws one story alone — its own
|
|
589
|
+
slices only, nothing from outside it — and `em render <root>.em --map` puts every story's real
|
|
590
|
+
diagram on one draggable canvas with the flow between stories (`after` thick, reads dashed and
|
|
591
|
+
labelled). **Storyline models (GH #421):** `storyline "Name" [after "Other"] [in "path.em"]` with no
|
|
592
|
+
body declares a storyline whose slices live in `<slug>/<slug>.em` — a file that starts with
|
|
593
|
+
`storyline "Name" [source "url"]` and holds slice blocks only — with its docs in `<slug>/slices/`;
|
|
594
|
+
the root is the compile unit (always give em the root), and `em storyline split <root>.em` turns
|
|
595
|
+
inline blocks into files, moving each story's docs. A storyline that reads another declares `after`
|
|
596
|
+
it (`storyline-reads-without-after`). `em export` carries `slice.storyline` and
|
|
555
597
|
`model.storylines[]`; `em query slices --storyline`; `em slice index` adds a Storyline column. Once
|
|
556
598
|
any storyline exists a slice outside every one is the advisory warning `slice-outside-storyline`.
|
|
557
599
|
`after "<other>"` reads the storyline as if its slices followed the referenced storyline's last
|
|
@@ -877,6 +919,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
|
|
|
877
919
|
<!-- GENERATED:validate-rules:start -- run `npm run docs:generate` to refresh, do not hand-edit -->
|
|
878
920
|
| Code | Severity | Title | Fix |
|
|
879
921
|
|---|---|---|---|
|
|
922
|
+
| `again-restates-sources` | warning | Continuation restates sources an earlier instance already read | Name only the events that changed the view since the previous instance on the `again` line; the view's field sources are the union across instances, so nothing is lost (docs/dsl.md#view--again). |
|
|
880
923
|
| `alternative-events-no-command` | error | Alternative events without a command | Add the command whose decision records one of the alternatives, in the same slice. |
|
|
881
924
|
| `arrow-backward` | error | Backward arrow | Restructure so the target comes later. |
|
|
882
925
|
| `arrow-unresolved-source` | error | Arrow source unresolved | Fix the arrow's source name. |
|
|
@@ -917,6 +960,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
|
|
|
917
960
|
| `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. |
|
|
918
961
|
| `frontmatter-coherence-implemented-without-link` | warning | Implemented without a link | Add `implementedIn` once the slice ships. |
|
|
919
962
|
| `frontmatter-invalid` | warning | Invalid or missing frontmatter | Add the required frontmatter keys, or add a frontmatter block. |
|
|
963
|
+
| `frontmatter-status-unrecognized` | warning | Unrecognized slice status | Set `status:` to one of draft, reviewed, ready-to-implement, implemented. |
|
|
920
964
|
| `grid-collision` | error | Band collision | Split the colliding elements into separate slices. |
|
|
921
965
|
| `invariant-without-scenario` | warning | Invariant cited by no scenario | Cite the INV id in the Given/When/Then (or title) of the scenario that exercises it, or add one. |
|
|
922
966
|
| `invariants/declared-in-both` | warning | Invariant declared in the model and in a slice doc | Keep the model's `invariant` line; in the doc, cite the ID and elaborate instead of restating it as a `**INV-…:**` rule. |
|
|
@@ -960,6 +1004,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
|
|
|
960
1004
|
| `storyline-after-cycle` | error | Storyline `after` cycle | Remove an `after` clause so no storyline comes after itself. |
|
|
961
1005
|
| `storyline-after-out-of-order` | error | Backward timeline (storyline declared above the one it comes after) | Move the storyline block below the storyline it comes `after`, or restructure the feed. |
|
|
962
1006
|
| `storyline-after-unknown` | error | `after` names an unknown storyline | Name a storyline declared in this model, or drop the `after` clause. |
|
|
1007
|
+
| `storyline-reads-without-after` | warning | Storyline reads another without declaring `after` it | Add `after "<the storyline it reads>"` to the reading storyline (or to one between them); if the two stories are parallel on purpose, accept it on the shape exit gate (docs/storyline.md). |
|
|
963
1008
|
| `system-manifest-invalid` | error | Seam manifest invalid | Fix the manifest: `systemSchemaVersion: "2.0"`, `models:` with a readable `source` per model, and nothing else (`seams:`/`owner:` moved into the models — run `em upgrade`). |
|
|
964
1009
|
| `system-manifest-outdated` | warning | Seam manifest is schema 1.0 | Run `em upgrade <model>.em --apply` once: it moves `seams:` into `consumes` clauses and `owner:` onto each model header, and rewrites the manifest to 2.0. |
|
|
965
1010
|
| `system-model-key-mismatch` | error | Manifest model key differs from the export's `model.key` | Rename the manifest's `models:` key to the computed key the message prints. |
|
|
@@ -219,7 +219,11 @@ drift into **CRUD laid on a timeline**; these rules stop that. Quoted lines are
|
|
|
219
219
|
reorganize are **secondary storylines** (chapters; Dilger: "one linear flow from left to right. No
|
|
220
220
|
exceptions… Linearize it!", "Model one process at a time, each with its own clear ending"). Never one
|
|
221
221
|
lifecycle block per entity. Name the main and every secondary storyline; a `storyline "Name" { … }`
|
|
222
|
-
block groups the slices (`dsl.md`, Storylines).
|
|
222
|
+
block groups the slices (`dsl.md`, Storylines). **A storyline is the unit a reader consumes, so it
|
|
223
|
+
is the unit an author edits too (GH #421):** the root model lists the storylines and their flow
|
|
224
|
+
(`storyline "Name" [after "Other"]`), and each story's slices live in `<slug>/<slug>.em` with its
|
|
225
|
+
docs in `<slug>/slices/`. Draft with inline blocks if that is faster, then `em storyline split
|
|
226
|
+
<root>.em` once the storylines are named — before docs are wired, so nothing moves twice.
|
|
223
227
|
- **Build from walkthroughs first.** The story already lives in the spec's walkthroughs, use cases and
|
|
224
228
|
user journeys. Build the timeline from those; the resource/API reference (OpenAPI, endpoint tables,
|
|
225
229
|
migrations) fills only the operations no walkthrough touches, and is read last. Headless: a persona
|
|
@@ -333,12 +337,41 @@ drift into **CRUD laid on a timeline**; these rules stop that. Quoted lines are
|
|
|
333
337
|
`view-lookup-source` is the mechanical, advisory half — it catches the cross-context cases with a few
|
|
334
338
|
false positives; a within-context lookup (`Tool Checked Out` read only to learn whose loan a later
|
|
335
339
|
event concerns) is invisible to it, so this check still has to be done by eye.
|
|
340
|
+
**Then split what is left into trigger and housekeeping** (Dilger: show the line from the event
|
|
341
|
+
that triggers the automation, not every event that touches its read model; GH #414). Of the
|
|
342
|
+
events a view legitimately reads, only the ones that **put work on the list** are the story — they
|
|
343
|
+
go in `from` and are drawn. Every event that merely **keeps a row current or removes it** (a due
|
|
344
|
+
date moved, an address changed, a loan returned) goes in `updates from`: still a source for
|
|
345
|
+
`derived from`, completeness and the generated built-from list, never an arrow. Write the to-do
|
|
346
|
+
list as `view Overdue Loans To Notify from "Tool Checked Out" updates from "Loan Extended",
|
|
347
|
+
"Neighbor Email Changed", "Tool Returned"`. The author chooses the triggers — `Holds To Fulfill`
|
|
348
|
+
keeps two (`from "Hold Placed", "Tool Returned"`) because both put a fulfillable hold on the
|
|
349
|
+
list. **Per-view check:** *which of these events is the reason this row appears? That is `from`;
|
|
350
|
+
everything else is `updates from`.* The render's "Kept current by" legend is the readback: if a
|
|
351
|
+
to-do view shows more than one or two inbound arrows, the split is missing. A State View that is
|
|
352
|
+
not a to-do list gets the same treatment — its screen's story is the event that creates the row.
|
|
336
353
|
- **Write side: children belong in the parent's context** when the child's invariants are checks against
|
|
337
354
|
the parent. A sub-resource schema shared across parent types is one event per parent on the parent's
|
|
338
355
|
context, not a shared aggregate — check the spec's event reference, not its resource section. Where
|
|
339
356
|
two slices record the same fact, repeat the event bare (same name, same context). *(em ruling)*
|
|
340
357
|
- **Repeat the view after every command that changes it** (`again`, span-1 — see State View above), so
|
|
341
|
-
the reader sees the information arrive rather than a view at the end.
|
|
358
|
+
the reader sees the information arrive rather than a view at the end. **A continuation names only
|
|
359
|
+
what changed:** `view X again from "E"` lists the event(s) that changed the view since the previous
|
|
360
|
+
instance — never the originating instance's whole source list again. Field sources are the union
|
|
361
|
+
across instances, so restating loses nothing and only draws the same arrows twice;
|
|
362
|
+
`again-restates-sources` (GH #424) is the mechanical half.
|
|
363
|
+
- **Storylines read as stories** (owner rulings 2026-11-03, after Dilger). (1) **A storyline that reads
|
|
364
|
+
another declares `after` it:** an event of story A feeding a view of story B means B comes after A
|
|
365
|
+
in the telling — write `storyline "B" after "A"` (or on a storyline between them); two stories that
|
|
366
|
+
really run in parallel read nothing from each other. `storyline-reads-without-after` (GH #420) is
|
|
367
|
+
the mechanical half. (2) **Review each story on its own render and on the canvas, never on the
|
|
368
|
+
full timeline:** `em render <root>.em --map` draws every storyline's real diagram as a frame with
|
|
369
|
+
the flow between stories (`after` thick, reads dashed and labelled by event); the full timeline is
|
|
370
|
+
for `em diff` and ordering checks, not for reading. A storyline render shows only the storyline —
|
|
371
|
+
nothing from outside it is drawn, so an inbound arrow you miss there is a connector on the canvas.
|
|
372
|
+
(3) **Housekeeping is not drawn** (`updates from`, above) and **continuations name only what
|
|
373
|
+
changed** (above): together these are what keeps a story's render to one line per cause. *(em
|
|
374
|
+
ruling, built on Dilger's "the line from the event that triggers it")*
|
|
342
375
|
- **Outcomes are events of one slice** (Dilger: "One decision. One event. One state change."; one
|
|
343
376
|
action with two outcomes is the legitimate exception): alternative events on one context in one slice
|
|
344
377
|
(`event A | B @Ctx`, GH #278), failure as a scenario first, a chapter only when core (it changes what the user does next, or has
|
|
@@ -174,25 +174,36 @@ including sessions started from bare `/em:event-modeling`.
|
|
|
174
174
|
<model-name>.svg # render target for em watch
|
|
175
175
|
README.md # overview + slice index (from templates/model-readme.md)
|
|
176
176
|
.event-modeling.md # resumable state (from templates/state.md)
|
|
177
|
+
constitution.md # implementation constitution (skipped, with a note, in a spec-kit project)
|
|
177
178
|
slices/<slice-name>.md # one rich slice doc per slice (from templates/slice.md)
|
|
178
|
-
|
|
179
|
-
#
|
|
179
|
+
contracts/<model-name>.tsp
|
|
180
|
+
# `em api generate`'s generated TypeSpec contract — never hand-edit
|
|
181
|
+
model-versions/v<N>.json # `em model version bump`'s design-version snapshots
|
|
182
|
+
conformance/
|
|
183
|
+
<date>-report.md # conform-phase reports (from templates/conformance-report.md)
|
|
184
|
+
bindings/<slice-key>.yaml
|
|
185
|
+
# per-slice bindings manifests (`em conform bindings check`, GH #449)
|
|
180
186
|
<model-name>-asis.em # conform-phase scratch model, regenerated per run — git-ignore this
|
|
181
187
|
```
|
|
182
188
|
|
|
183
|
-
When creating a new model, run `em scaffold <model-name>` — it creates `<model-name
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
mechanical fields (model path, `Current phase: discover`, `Current step: 1`, dates) for you,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
model
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
`
|
|
189
|
+
When creating a new model, run `em scaffold <model-name>` — it creates `<model-name>/`, writes
|
|
190
|
+
the four starter files in one step (`<model-name>.em`, `README.md`, `.event-modeling.md`,
|
|
191
|
+
`constitution.md`), filling the Model Name/model-name placeholders and the state file's
|
|
192
|
+
mechanical fields (model path, `Current phase: discover`, `Current step: 1`, dates) for you, and
|
|
193
|
+
also creates the standard `slices/`, `contracts/`, `model-versions/` and `conformance/bindings/`
|
|
194
|
+
directories — each seeded with a one-line README (never a `.gitkeep`) naming what lands there and
|
|
195
|
+
which command writes it. There's nothing left to hand-copy or fill in. Refuses if
|
|
196
|
+
`<model-name>/` already exists; pass `--force` to overwrite. You'll usually replace the
|
|
197
|
+
scaffolded `.em`'s content as the discovery conversation builds up the real model — it starts out
|
|
198
|
+
as the same starter model `em init` writes, titled from `<model-name>`.
|
|
199
|
+
Those directories exist from the start, but their real content doesn't — `slices/*.md` slice
|
|
200
|
+
docs come from the `slice` phase (`em:design`), `conformance/<date>-report.md`
|
|
201
|
+
reports from the `conform` phase (`em:conform`), and `contracts/*.tsp` /
|
|
202
|
+
`model-versions/*.json` / `conformance/bindings/*.yaml` only once `em api generate` / `em model
|
|
203
|
+
version bump` / an implementing agent's bindings manifest actually run. `<model-name>-asis.em` is
|
|
204
|
+
separate conform-phase scratch, not part of the scaffold at all; `em conform-scope --seed-asis`
|
|
205
|
+
creates it and gitignores `*-asis.em` for you (see `em:conform`'s
|
|
206
|
+
`reference/conform.md`) — it's scratch, never committed.
|
|
196
207
|
|
|
197
208
|
### Multi-model projects
|
|
198
209
|
|
|
@@ -57,7 +57,7 @@ to decide `frontmatter-invalid` without re-deriving frontmatter-shape rules of i
|
|
|
57
57
|
| `schemaVersion` | integer | `1` (current dialect version) | its *value* is not read back by any `em` command today (reserved) — but its *presence* is required by `hasUsableFrontmatter()`: a doc omitting it is `frontmatter-invalid` (see below) |
|
|
58
58
|
| `pattern` | string | `state-change` \| `state-view` \| `automation` \| `translation` | authored/informational only — `em catalog` and `em export` both derive pattern from the `.em` AST instead (`em export`'s `slice.pattern`, schema `1.4`) |
|
|
59
59
|
| `swimlane` | string | free text, `<Persona> → <Context>` | display-only |
|
|
60
|
-
| `status` | string, case-insensitive | `draft` \| `reviewed` \| `ready-to-implement` \| `implemented` | written by `em slice review` (→ `reviewed`, MIL-201), `em slice ratify`/`em slice reratify` (→ `ready-to-implement`) and `em slice mark-implemented` (→ `implemented`); read by `em catalog`, `em render`/`em watch` header coloring; joined into `em export`'s `slice.doc.status` (schema `1.4`); paired with `implementedIn` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) |
|
|
60
|
+
| `status` | string, case-insensitive | `draft` \| `reviewed` \| `ready-to-implement` \| `implemented` | written by `em slice review` (→ `reviewed`, MIL-201), `em slice ratify`/`em slice reratify` (→ `ready-to-implement`) and `em slice mark-implemented` (→ `implemented`); read by `em catalog`, `em render`/`em watch` header coloring; joined into `em export`'s `slice.doc.status` (schema `1.4`); paired with `implementedIn` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)); any other value is read as written but `em validate` warns `frontmatter-status-unrecognized` (GH #411) |
|
|
61
61
|
| `version` | integer | positive integer, starts at `1` | joined into `em export`'s `slice.doc.version` (schema `1.4`, MIL-91) |
|
|
62
62
|
| `implementedIn` | string | free text (PR/commit link) | joined into `em export`'s `slice.doc.implementedIn` (schema `1.4`, MIL-91); paired with `status` to compute `slice.doc.driftSignal` (schema `1.5`, MIL-85) and `em validate`'s frontmatter-coherence check (MIL-85 — see [validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence)) |
|
|
63
63
|
| `split-from` | single ref | `<slice-key>@v<N>` | joined into `em export`'s `slice.doc.splitFrom` (schema `1.4`, MIL-91) and `em diff`'s `slice-added` entries (schema `1.6`, MIL-84); current-tree referential checks by `em validate` (MIL-84 — see [validation.md#lineage](https://github.com/milehimikey/em/blob/main/docs/validation.md#lineage)) |
|
|
@@ -473,6 +473,7 @@ re-run `em slice sync`.
|
|
|
473
473
|
|
|
474
474
|
| Region | Section | Body |
|
|
475
475
|
|---|---|---|
|
|
476
|
+
| `em-slice-story` | right under `` | one line: `Step N of M in **<Storyline>** · after <Prev> · before <Next> · <Pattern>` (Prev and Next link to the neighbouring slices' docs; a continuation neighbour is named, not linked) — where the slice sits in its story; the one region `em slice sync` inserts when missing on an unsigned doc (see `em slice sync` in the CLI reference) |
|
|
476
477
|
| `em-slice-command` | `## Command / Input` | `**Command:** \`Name\`` + `\| Field \| Type \| Required \| Rules / Validation \|` (Required is `no` for a `name?: Type` field) |
|
|
477
478
|
| `em-slice-event` | `## Event(s) Emitted` | `**Event:** \`Name\` → context \`Ctx\`` + `\| Field \| Type \| Immutable Fact? \| Source / Notes \|` (Source: the same-slice command field it is copied from; failing that, the command from another slice that also records the event, written `not supplied by this command (from \`A\`, slice "Create")` (GH #344 / #362, the same union `fields-completeness/event-field-no-source` checks; worded as a statement about this slice, since the repeat's own command does not take the field); `assigned` for a system-assigned field, else `{{ }}`) |
|
|
478
479
|
| `em-slice-view` | `## Read Model / View` | `- **View:** \`Name\` built from events: …` (the union across the originating and every `again` instance; each later event is annotated `(via "<slice>")` with the slice of the instance that adds it) + `\| Field \| Type \| Source / Notes \|` (Source: the source event, from that union, carrying the field, `Derived` for a `derived` field, `Collection of \`T\`, fed by …` for a `T[] derived from …` field — followed by one neutral Given/Then scenario stub per named event under the table, to copy into `## Scenarios` and complete there (the region itself stays read-only); `em slice sync` retires a stub once an authored scenario covers that field and event, by the stub's `<field> after <Event>` title or by a Given naming the event with the field named in the block (GH #368), and drops the `Scenario stubs for` line when none is left — else `{{ }}`) |
|
|
@@ -32,14 +32,22 @@ Change slice is paired with the State View slice that projects its event. A read
|
|
|
32
32
|
along the timeline (`view X again`) shows the same projection at a later point; the instances are
|
|
33
33
|
never connected to one another.
|
|
34
34
|
|
|
35
|
+
## Storylines
|
|
36
|
+
<!-- Generated — `em slice index {{model-name}}.em` writes one row per storyline between the
|
|
37
|
+
markers (count, implemented, diagram); the canvas is `em render {{model-name}}.em --map`. -->
|
|
38
|
+
<!-- GENERATED:storylines:start -->
|
|
39
|
+
| Storyline | Slices | Implemented | Diagram |
|
|
40
|
+
|-----------|--------|-------------|---------|
|
|
41
|
+
<!-- GENERATED:storylines:end -->
|
|
42
|
+
|
|
35
43
|
## Slices
|
|
36
44
|
<!-- The canonical slice index — the ONE place slices are enumerated (the state file
|
|
37
45
|
points here rather than keeping its own copy). Generated — run
|
|
38
46
|
`em slice index {{model-name}}.em` to (re)write the table below from the model and its
|
|
39
47
|
slice docs; never hand-edit between the markers. -->
|
|
40
48
|
<!-- GENERATED:slices:start -->
|
|
41
|
-
| # | Slice | Pattern | Status |
|
|
42
|
-
|
|
49
|
+
| # | Slice | Pattern | Status | Design doc |
|
|
50
|
+
|---|-------|---------|--------|------------|
|
|
43
51
|
<!-- GENERATED:slices:end -->
|
|
44
52
|
|
|
45
53
|
## Status
|
|
@@ -80,6 +80,10 @@ implementedIn: {{PR/commit link — fill in once status is `implemented`}}
|
|
|
80
80
|
|
|
81
81
|

|
|
82
82
|
|
|
83
|
+
<!-- GENERATED:em-slice-story:start — generated from the model by `em slice sync`; do not hand-edit -->
|
|
84
|
+
_Run `em slice sync` to place this slice in its story._
|
|
85
|
+
<!-- GENERATED:em-slice-story:end -->
|
|
86
|
+
|
|
83
87
|
<!-- Only on re-ratification of an already-`implemented` slice — omit this whole section on a
|
|
84
88
|
slice's first version (v1 has no delta yet). The heading is always the literal `## Delta`,
|
|
85
89
|
never `## Delta: vX → vY` — hop metadata is a display line inside the section instead (see
|
|
@@ -170,7 +174,9 @@ an event directly.}}
|
|
|
170
174
|
own rows, a lookup of some other entity's name/email/etc. so it can display it — that is the
|
|
171
175
|
views-as-lookups smell (methodology reference, "Views as lookups"): the field belongs on the
|
|
172
176
|
event that creates this view's row instead, copied from state the command/decider already
|
|
173
|
-
reads to enforce its own invariants. Fix the `.em`, not the prose.
|
|
177
|
+
reads to enforce its own invariants. Fix the `.em`, not the prose. The built-from list names
|
|
178
|
+
every source, trigger and housekeeping alike; in the `.em` only the triggering event(s) sit in
|
|
179
|
+
`from`, the rest in `updates from` (methodology reference, "trigger and housekeeping"). -->
|
|
174
180
|
<!-- GENERATED:em-slice-view:start — generated from the model by `em slice sync`; do not hand-edit -->
|
|
175
181
|
- **View:** `{{View Name}}` built from events: {{"Event A", "Event B"}}
|
|
176
182
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: conform
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.22.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use when checking a ratified event model (and its slice docs) for drift against the codebase
|
|
6
6
|
that implements it — advisory only, never a gate, never an unprompted edit — or when walking
|
|
@@ -29,9 +29,12 @@ duplicating them.
|
|
|
29
29
|
In short: run `em conform-scope <model-name>.em --repo <target-repo-path> --seed-asis` to
|
|
30
30
|
compute the in-scope slice set (diff-scoped by default, via `Last conformance:` + the target
|
|
31
31
|
repo's changed paths mapped through each slice doc's `implementedIn:`) and seed the scratch
|
|
32
|
-
model in one step; for each in-scope slice,
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
model in one step; for each in-scope slice, check for a `conformance/bindings/<key>.yaml` first
|
|
33
|
+
(GH #450) — verify it with `em conform bindings check` and scope the walk through it when it
|
|
34
|
+
exists, backfill it from this walk's own evidence when it doesn't — then gather code evidence
|
|
35
|
+
*before* comparing to the model or doc; write the as-is picture into that scratch model
|
|
36
|
+
(`<model-name>-asis.em`, reusing the canonical model's names wherever the code matches them);
|
|
37
|
+
run `em diff <model-name>.em
|
|
35
38
|
<model-name>-asis.em --json` and let `em` decide the structural deltas; classify every finding
|
|
36
39
|
(real drift / model gap / internal inconsistency / uncertainty) with cited evidence; write
|
|
37
40
|
`conformance/<date>-report.md` **and its structured twin `conformance/<date>-findings.json`**
|
|
@@ -95,7 +95,7 @@ e.g. a PR link, deterministically matches nothing). It prints one JSON document
|
|
|
95
95
|
```json
|
|
96
96
|
{ "lastConformance": { "date": "...", "revision": "..." } | null,
|
|
97
97
|
"changedPaths": ["..."],
|
|
98
|
-
"candidateSlices": [ { "key": "checkout", "matchedBy": "implementedIn", "paths": ["..."] } ],
|
|
98
|
+
"candidateSlices": [ { "key": "checkout", "matchedBy": "implementedIn", "paths": ["..."], "file": null } ],
|
|
99
99
|
"unmappedPaths": ["..."] }
|
|
100
100
|
```
|
|
101
101
|
|
|
@@ -105,6 +105,20 @@ your in-scope set for step 2. If `lastConformance` is `null` (first run) or you
|
|
|
105
105
|
`changedPaths`/`unmappedPaths` both empty) — the tool makes this call for you, no need to
|
|
106
106
|
special-case it yourself.
|
|
107
107
|
|
|
108
|
+
**Storyline models (GH #451).** A model may split its storylines into their own files — a root
|
|
109
|
+
declares `storyline "Name" [after "Other"]` with no body, and the storyline's slices live in
|
|
110
|
+
`<slug>/<slug>.em` (`docs/dsl.md`'s "Storyline models" section). `em conform-scope` already
|
|
111
|
+
compiles the root plus every storyline file it declares as ONE model
|
|
112
|
+
(the same way every other `em` command does — a storyline file never compiles on its own), so
|
|
113
|
+
scoping via `implementedIn` needs no special handling. What DOES change: each `candidateSlice`'s
|
|
114
|
+
`file` names the storyline file that slice's body lives in — posix, relative to the model's
|
|
115
|
+
directory — or `null` for a slice in the root file. This is which physical file you anchor on in
|
|
116
|
+
step 2 and which scratch file you write the as-is picture into in step 3 (`--seed-asis` seeds one
|
|
117
|
+
scratch file per physical file, not just the root — see there). Don't confuse a slice's `file`
|
|
118
|
+
with its doc's path: slice docs for a storyline model live under that storyline's own
|
|
119
|
+
`slices/` folder, but `em` finds them by key across every folder regardless, so doc resolution
|
|
120
|
+
itself needs no extra care.
|
|
121
|
+
|
|
108
122
|
**`unmappedPaths` is your judgment queue, not a dead end.** A changed path the tool couldn't
|
|
109
123
|
place mechanically (`implementedIn` stale, a URL-only link, code that moved) still might belong
|
|
110
124
|
to an in-scope slice — a quick Grep for the path's obvious symbols against the slice docs/model
|
|
@@ -120,6 +134,31 @@ keeps a recurring conform run cheap; don't invent a smarter trigger.
|
|
|
120
134
|
|
|
121
135
|
For each slice in scope:
|
|
122
136
|
|
|
137
|
+
**Check for an existing bindings file first (GH #450).** `conformance/bindings/<slice-key>.yaml`,
|
|
138
|
+
beside the model, records this slice's own prior evidence — file and identifier per model
|
|
139
|
+
element, and per `INV-*` invariant's enforcement/test sites (`docs/bindings.md`'s schema). Two
|
|
140
|
+
cases, mutually exclusive:
|
|
141
|
+
|
|
142
|
+
- **It exists:** verify it before walking anything by hand —
|
|
143
|
+
`em conform bindings check <model-name>.em --repo <target-repo-path> --slice <slice-key>`.
|
|
144
|
+
A binding that still checks out is evidence you don't need to re-derive; **scope the rest of
|
|
145
|
+
this slice's walk through it** — re-read only what the check reports failing (`file not
|
|
146
|
+
found`/`identifier not found`, an unknown `ref`) plus whatever `em conform-scope`'s
|
|
147
|
+
`changedPaths` (step 1) says moved since `implementedIn`. This is what turns a recurring
|
|
148
|
+
conform walk incremental instead of a full re-derivation every time — don't throw that away by
|
|
149
|
+
re-walking evidence the file already gives you clean.
|
|
150
|
+
- **It doesn't exist:** the common shape for a slice an external or ticket-driven implementer
|
|
151
|
+
shipped without ever running `em` (`docs/bindings.md` is written so that implementer can still
|
|
152
|
+
author one by hand from its own ticket spec; this backfill is the safety net for the ones that
|
|
153
|
+
didn't, or for any slice that shipped before GH #450). Walk the slice normally (the numbered
|
|
154
|
+
steps below) and, once you've gathered that evidence, **write
|
|
155
|
+
`conformance/bindings/<slice-key>.yaml` from it** — the same file an implementer would have
|
|
156
|
+
written, now backfilled from your conform evidence instead of from the implementer's own
|
|
157
|
+
knowledge of the code. This is advisory tooling catching up a gap, not a model or doc edit: it
|
|
158
|
+
needs no ratification and doesn't change this phase's advisory-only stance (see "Stance"
|
|
159
|
+
above) — you're recording where code already lives, not deciding what it should do. From the
|
|
160
|
+
next conform run on, this slice follows the "it exists" branch above.
|
|
161
|
+
|
|
123
162
|
1. Read the slice's **doc** (`slices/<slice-name>.md`) alongside its elements in the `.em`.
|
|
124
163
|
This tells you the vocabulary to anchor on and the claims to check — not the answer.
|
|
125
164
|
2. Walk the code and **record evidence before comparing**:
|
|
@@ -140,6 +179,20 @@ For each slice in scope:
|
|
|
140
179
|
as missing. This is the *general*, idiom-level version of the next bullet — prefer citing
|
|
141
180
|
an actual `divergence` annotation when the element carries one; fall back to this idiom
|
|
142
181
|
note only when it doesn't.
|
|
182
|
+
- **`updates from` sources (GH #451) — housekeeping evidence, not trigger evidence.** A view
|
|
183
|
+
declared `view X from "A" updates from "B", "C"` (`docs/dsl.md`'s `updates from` section)
|
|
184
|
+
splits its sources into what put a row on the list (`from`) and what keeps an existing row
|
|
185
|
+
current or removes it (`updates from`). The two need different evidence: a `from` source is
|
|
186
|
+
what an automation/processor reacting to this view should fire on, or what a reader expects
|
|
187
|
+
to see a NEW row appear for — missing evidence there is the usual structural/spec drift. A
|
|
188
|
+
`updates from` source is expected to have an update/delete handler for the SAME row, never a
|
|
189
|
+
"new item created" or "automation triggered" site — don't report the absence of trigger-type
|
|
190
|
+
evidence for an `updates from` source as drift; that is literally what the clause says it
|
|
191
|
+
isn't. `em export`'s `from` list is the union of both (so `em diff`'s structural comparison
|
|
192
|
+
sees every source regardless of which list it's in — folding `from`/`updates from` into each
|
|
193
|
+
other is NOT itself a structural change `em diff` would catch), but the *diagram* draws no
|
|
194
|
+
arrow for an `updates from` source and the *evidence walk* above should read the same way:
|
|
195
|
+
check it as "does this row get kept current/removed", not "does this cause a new row".
|
|
143
196
|
- **Accepted divergences:** before treating any finding as drift, check whether the
|
|
144
197
|
relevant canonical element already carries a `divergence "..."` annotation (visible in
|
|
145
198
|
the `.em`, in `em export --json`'s `divergence` field, or — for a structural finding — as
|
|
@@ -162,8 +215,15 @@ For each slice in scope:
|
|
|
162
215
|
3. Write what you found into the scratch model (`<model-name>-asis.em`, see below) — start it
|
|
163
216
|
with `em conform-scope <model-name>.em --repo <target-repo-path> --seed-asis`, which
|
|
164
217
|
byte-copies the canonical `.em` to `<model-name>-asis.em` and ensures `*-asis.em` is
|
|
165
|
-
gitignored (idempotent — safe to pass on every run). **
|
|
166
|
-
|
|
218
|
+
gitignored (idempotent — safe to pass on every run). **Storyline models (GH #451):** when the
|
|
219
|
+
slice you're rewriting has a non-null `file` in step 1's `candidateSlices` (its body lives in
|
|
220
|
+
a storyline file, not the root), `--seed-asis` also wrote a scratch copy of THAT file — the
|
|
221
|
+
`seeded.storylineFiles` entry naming it — and the copied root's declaration already points at
|
|
222
|
+
it. Edit that storyline file's own `-asis` sibling, never `<model-name>-asis.em` (it has no
|
|
223
|
+
body for that slice to rewrite) and never the canonical storyline file (that corrupts the
|
|
224
|
+
real model, not a scratch copy). **Then replace only the in-scope slices** with the as-is
|
|
225
|
+
picture, leaving out-of-scope slices byte-identical, in whichever scratch file(s) hold them.
|
|
226
|
+
This is not an optimization:
|
|
167
227
|
`em diff` matches slices by name, so a scratch model containing *only* the in-scope slices
|
|
168
228
|
reports every other canonical slice as `slice-removed` plus one `element-removed` per
|
|
169
229
|
element — a phantom-removal flood on every diff-scoped run, which is the exact false drift
|
|
@@ -406,7 +466,12 @@ each call adds its own stamp rather than overwriting the last.
|
|
|
406
466
|
directory as `.event-modeling.md`). Always seeded as a copy of the canonical model with only
|
|
407
467
|
the in-scope slices rewritten (step 2) — `em conform-scope --seed-asis` does the seeding and
|
|
408
468
|
the gitignoring (`*-asis.em`, idempotent) mechanically, never built up from nothing and never
|
|
409
|
-
hand-maintained. It's regenerated every conform run.
|
|
469
|
+
hand-maintained. It's regenerated every conform run. **Storyline models (GH #451):** a split
|
|
470
|
+
model gets ONE scratch file per physical file — `<model-name>-asis.em` for the root, plus a
|
|
471
|
+
`-asis` sibling for every storyline file the root declares (`seeded.storylineFiles` names
|
|
472
|
+
them) — never just the root; the copied root's `in` clause already points at each sibling, so
|
|
473
|
+
`em diff <model-name>.em <model-name>-asis.em` reads the whole scratch set as one model, same
|
|
474
|
+
as the canonical root reads the whole canonical set.
|
|
410
475
|
- **Report location:** `conformance/<YYYY-MM-DD>-report.md` in the model directory. One file
|
|
411
476
|
per run; don't overwrite a prior date's report. Its findings go in the same-stem `-findings.json`; gates read every same-revision findings file, so per-slice runs may write `<date>-<name>-findings.json`, and a ruling is written to the file the report names. The same-revision lookup uses the report's OWN revision (its `Target repo: … @ <rev>` header line; the caller's `<rev>` / `--as-of` only when the report has none).
|
|
412
477
|
- **Red note wording:** `issue "conformance: <what's wrong, plainly>"` — plain enough that
|
|
@@ -448,6 +513,13 @@ whatever cadence the user wants (see `docs/ci.md` for a scheduled-run recipe).
|
|
|
448
513
|
- **A scratch model holding only the in-scope slices** — `em diff` reads every omitted
|
|
449
514
|
canonical slice as removed, so a diff-scoped run drowns in phantom removals. Seed from the
|
|
450
515
|
canonical model and rewrite only the slices you walked.
|
|
516
|
+
- **Editing a storyline file's canonical copy as if it were the scratch model (GH #451)** — for
|
|
517
|
+
a slice whose `file` isn't the root, the scratch copy is that storyline file's OWN `-asis`
|
|
518
|
+
sibling (`seeded.storylineFiles`), never `<model-name>-asis.em` (no body there to rewrite) and
|
|
519
|
+
never the canonical storyline file itself (that's the model, not a scratch copy of it).
|
|
520
|
+
- **Treating an `updates from` source like a `from` source during the evidence walk** — a
|
|
521
|
+
missing "new row appears" or "automation fires" site is not drift for a source the view only
|
|
522
|
+
lists under `updates from`; check it for row-update/removal evidence instead (step 2).
|
|
451
523
|
- **Reporting uncertainty as drift** — "I couldn't find the enforcement site" is an
|
|
452
524
|
uncertainty, never a finding against the model.
|
|
453
525
|
- **Editing the model or a slice doc without ratification** — conform proposes; the user
|
|
@@ -458,6 +530,12 @@ whatever cadence the user wants (see `docs/ci.md` for a scheduled-run recipe).
|
|
|
458
530
|
- **Re-deriving what `em diff` already decided** — once you have the JSON, use it; don't
|
|
459
531
|
eyeball the two `.em` files yourself looking for structural differences the tool already
|
|
460
532
|
computed for you.
|
|
533
|
+
- **Re-deriving evidence a verified bindings file already gives you (GH #450)** — when
|
|
534
|
+
`em conform bindings check` passes for a slice's `conformance/bindings/<key>.yaml`, walking
|
|
535
|
+
every element and invariant by hand anyway defeats the point of the file; scope through it
|
|
536
|
+
(step 2) instead.
|
|
537
|
+
- **Skipping the backfill because a slice has no bindings file** — the absence is the signal to
|
|
538
|
+
write one from this walk's own evidence, not a reason to leave the gap for next time.
|
|
461
539
|
- **Re-litigating an accepted divergence** — a non-null `acceptedDivergence` on a diff entry
|
|
462
540
|
(or a `divergence` annotation on the element behind a spec/internal finding) means this exact
|
|
463
541
|
question was already reasoned through and ratified. Classify it as Accepted divergence and
|