@milehimikey/em 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +1 -1
  2. package/.claude/skills/event-modeling-conform/SKILL.md +5 -1
  3. package/.claude/skills/event-modeling-design/SKILL.md +41 -14
  4. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  5. package/.claude/skills/event-modeling-implement/SKILL.md +24 -7
  6. package/.claude/skills/event-modeling-implement/reference/implement.md +227 -19
  7. package/.claude/skills/event-modeling-review/SKILL.md +14 -3
  8. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +80 -15
  9. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +43 -1
  10. package/.claude/skills/event-modeling-shared/templates/constitution.md +118 -0
  11. package/.claude/skills/event-modeling-shared/templates/model-readme.md +2 -2
  12. package/.claude/skills/event-modeling-shared/templates/slice.md +4 -0
  13. package/dist/catalog/build.js +42 -1
  14. package/dist/catalog/build.js.map +1 -1
  15. package/dist/catalog/coverage.d.ts +15 -0
  16. package/dist/catalog/coverage.js +34 -0
  17. package/dist/catalog/coverage.js.map +1 -0
  18. package/dist/catalog/docJoin.d.ts +19 -2
  19. package/dist/catalog/docJoin.js +30 -4
  20. package/dist/catalog/docJoin.js.map +1 -1
  21. package/dist/catalog/docModelConsistencyValidate.js +1 -1
  22. package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
  23. package/dist/catalog/noteBindingValidate.js +18 -0
  24. package/dist/catalog/noteBindingValidate.js.map +1 -1
  25. package/dist/catalog/orphanedSliceDocValidate.d.ts +8 -1
  26. package/dist/catalog/orphanedSliceDocValidate.js +33 -6
  27. package/dist/catalog/orphanedSliceDocValidate.js.map +1 -1
  28. package/dist/catalog/pages.d.ts +16 -1
  29. package/dist/catalog/pages.js +16 -4
  30. package/dist/catalog/pages.js.map +1 -1
  31. package/dist/catalog/sliceDoc.d.ts +8 -0
  32. package/dist/catalog/sliceDoc.js +2 -0
  33. package/dist/catalog/sliceDoc.js.map +1 -1
  34. package/dist/catalog/sliceReadyValidate.d.ts +12 -1
  35. package/dist/catalog/sliceReadyValidate.js +4 -4
  36. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  37. package/dist/cli/agentsMd.js +9 -3
  38. package/dist/cli/agentsMd.js.map +1 -1
  39. package/dist/cli/ciInit.js +3 -0
  40. package/dist/cli/ciInit.js.map +1 -1
  41. package/dist/cli/conformScope.js +10 -3
  42. package/dist/cli/conformScope.js.map +1 -1
  43. package/dist/cli/coverage.d.ts +34 -9
  44. package/dist/cli/coverage.js +61 -24
  45. package/dist/cli/coverage.js.map +1 -1
  46. package/dist/cli/ledgerCheck.d.ts +72 -0
  47. package/dist/cli/ledgerCheck.js +122 -1
  48. package/dist/cli/ledgerCheck.js.map +1 -1
  49. package/dist/cli/markImplemented.js +12 -1
  50. package/dist/cli/markImplemented.js.map +1 -1
  51. package/dist/cli/ratify.d.ts +37 -5
  52. package/dist/cli/ratify.js +123 -10
  53. package/dist/cli/ratify.js.map +1 -1
  54. package/dist/cli/reratify.js +26 -6
  55. package/dist/cli/reratify.js.map +1 -1
  56. package/dist/cli/review.d.ts +38 -0
  57. package/dist/cli/review.js +171 -0
  58. package/dist/cli/review.js.map +1 -0
  59. package/dist/cli/sliceIndex.d.ts +3 -0
  60. package/dist/cli/sliceIndex.js +14 -8
  61. package/dist/cli/sliceIndex.js.map +1 -1
  62. package/dist/cli/sliceLink.js +16 -0
  63. package/dist/cli/sliceLink.js.map +1 -1
  64. package/dist/cli/stateFile.d.ts +10 -0
  65. package/dist/cli/stateFile.js +14 -0
  66. package/dist/cli/stateFile.js.map +1 -1
  67. package/dist/cli/status.d.ts +55 -3
  68. package/dist/cli/status.js +92 -19
  69. package/dist/cli/status.js.map +1 -1
  70. package/dist/cli.js +203 -47
  71. package/dist/cli.js.map +1 -1
  72. package/dist/emit/coverageJson.d.ts +2 -2
  73. package/dist/emit/coverageJson.js +6 -2
  74. package/dist/emit/coverageJson.js.map +1 -1
  75. package/dist/emit/freshnessJson.d.ts +1 -1
  76. package/dist/emit/freshnessJson.js +5 -1
  77. package/dist/emit/freshnessJson.js.map +1 -1
  78. package/dist/emit/json.d.ts +45 -1
  79. package/dist/emit/json.js +82 -3
  80. package/dist/emit/json.js.map +1 -1
  81. package/dist/emit/ledgerJson.d.ts +1 -1
  82. package/dist/emit/ledgerJson.js +6 -1
  83. package/dist/emit/ledgerJson.js.map +1 -1
  84. package/dist/emit/queryJson.d.ts +1 -1
  85. package/dist/emit/queryJson.js +5 -1
  86. package/dist/emit/queryJson.js.map +1 -1
  87. package/dist/emit/statusJson.d.ts +1 -1
  88. package/dist/emit/statusJson.js +10 -1
  89. package/dist/emit/statusJson.js.map +1 -1
  90. package/dist/emit/validateJson.d.ts +4 -3
  91. package/dist/emit/validateJson.js +10 -3
  92. package/dist/emit/validateJson.js.map +1 -1
  93. package/dist/mcp/server.js +47 -22
  94. package/dist/mcp/server.js.map +1 -1
  95. package/dist/model/continuation.d.ts +23 -0
  96. package/dist/model/continuation.js +49 -0
  97. package/dist/model/continuation.js.map +1 -0
  98. package/dist/model/edges.d.ts +18 -3
  99. package/dist/model/edges.js +34 -0
  100. package/dist/model/edges.js.map +1 -1
  101. package/dist/model/model.d.ts +4 -0
  102. package/dist/model/model.js +1 -0
  103. package/dist/model/model.js.map +1 -1
  104. package/dist/model/queryIndex.d.ts +5 -2
  105. package/dist/model/queryIndex.js +5 -2
  106. package/dist/model/queryIndex.js.map +1 -1
  107. package/dist/model/rules.d.ts +25 -0
  108. package/dist/model/rules.js +25 -0
  109. package/dist/model/rules.js.map +1 -1
  110. package/dist/model/sliceDocIndex.d.ts +12 -5
  111. package/dist/model/sliceDocIndex.js +18 -7
  112. package/dist/model/sliceDocIndex.js.map +1 -1
  113. package/dist/model/validate.js +82 -1
  114. package/dist/model/validate.js.map +1 -1
  115. package/dist/parser/ast.d.ts +21 -0
  116. package/dist/parser/parser.js +96 -32
  117. package/dist/parser/parser.js.map +1 -1
  118. package/dist/render/drawEdges.js +7 -0
  119. package/dist/render/drawEdges.js.map +1 -1
  120. package/dist/render/drawNotes.d.ts +13 -3
  121. package/dist/render/drawNotes.js +96 -20
  122. package/dist/render/drawNotes.js.map +1 -1
  123. package/dist/render/render.js +6 -5
  124. package/dist/render/render.js.map +1 -1
  125. package/dist/render/sliceStatus.d.ts +3 -3
  126. package/dist/render/sliceStatus.js +27 -29
  127. package/dist/render/sliceStatus.js.map +1 -1
  128. package/dist/templates.d.ts +14 -1
  129. package/dist/templates.js +141 -5
  130. package/dist/templates.js.map +1 -1
  131. package/dist/util/localDate.d.ts +2 -0
  132. package/dist/util/localDate.js +17 -0
  133. package/dist/util/localDate.js.map +1 -0
  134. package/package.json +1 -1
@@ -11,20 +11,27 @@
11
11
  // docs for them (first one gets the real file, the second honestly gets null).
12
12
  //
13
13
  // MIL-121 fallback: when a slice has no own `slices/<key>.md`, scan the `slices/` directory for
14
- // a sibling doc whose frontmatter `covers:` names this slice's key, and borrow IT. This
15
- // is filename+frontmatter discovery only — same convention as the direct lookup above, and
16
- // still never reads `note` (the tested invariant test/catalog.e2e.test.ts pins for `em catalog`'s
17
- // parallel doc discovery) — a slice's `note "slices/<other>.md"` cross-binding declaration is
18
- // docJoin.ts's concern (`em export`/`em validate --slice-ready`), not this module's.
19
- import { existsSync, readdirSync, readFileSync } from "node:fs";
14
+ // a sibling doc whose frontmatter `covers:` names this slice's key, and borrow IT. The scan
15
+ // itself (readCoverageDocs) is shared with `em catalog`'s own covered-slice handling (MIL-137,
16
+ // src/catalog/coverage.ts) so the two never disagree about which doc covers a slice.
17
+ //
18
+ // MIL-208 fallback (checked last, after own file and the covers: scan both come up empty): a
19
+ // continuation slice (an again-view-only slice) borrows the ORIGINATING slice's own file
20
+ // directly — same predicate/resolution as catalog/docJoin.ts's note-gated join, model/
21
+ // continuation.ts, just applied to this module's filename-convention resolution instead of
22
+ // notes.
23
+ import { existsSync, readFileSync } from "node:fs";
20
24
  import { join } from "node:path";
21
25
  import { computeRefs } from "../model/refs.js";
26
+ import { continuationOf } from "../model/continuation.js";
27
+ import { readCoverageDocs } from "../catalog/coverage.js";
22
28
  import { parseSliceDoc } from "../catalog/sliceDoc.js";
23
- /** Each slice's resolved design doc (own `slices/<key>.md`, or the MIL-121 covering doc, or
24
- * null), same order as model.slices (and so the same order as the header row's columns —
25
- * see layout/grid.ts's sliceNames). */
29
+ /** Each slice's resolved design doc (own `slices/<key>.md`, the MIL-121 covering doc, the
30
+ * MIL-208 originating slice's own file, or null), same order as model.slices (and so the same
31
+ * order as the header row's columns — see layout/grid.ts's sliceNames). */
26
32
  export function readSliceDocs(model, baseDir) {
27
- const { sliceKeys } = computeRefs(model);
33
+ const refs = computeRefs(model);
34
+ const { sliceKeys } = refs;
28
35
  const slicesDir = join(baseDir, "slices");
29
36
  let coverage = null; // built lazily, only if ever needed
30
37
  return model.slices.map((_slice, i) => {
@@ -33,7 +40,16 @@ export function readSliceDocs(model, baseDir) {
33
40
  if (existsSync(docPath))
34
41
  return parseSliceDoc(readFileSync(docPath, "utf8"));
35
42
  coverage ??= readCoverageDocs(slicesDir);
36
- return coverage.get(sliceKey) ?? null;
43
+ const covering = coverage.get(sliceKey)?.doc;
44
+ if (covering)
45
+ return covering;
46
+ const continuation = continuationOf(model, refs, i);
47
+ if (continuation) {
48
+ const originatingPath = join(slicesDir, `${continuation.sliceKey}.md`);
49
+ if (existsSync(originatingPath))
50
+ return parseSliceDoc(readFileSync(originatingPath, "utf8"));
51
+ }
52
+ return null;
37
53
  });
38
54
  }
39
55
  /** One status per slice, same order/resolution as readSliceDocs. null means no doc was found
@@ -41,22 +57,4 @@ export function readSliceDocs(model, baseDir) {
41
57
  export function readSliceStatuses(model, baseDir) {
42
58
  return readSliceDocs(model, baseDir).map((doc) => doc?.status ?? null);
43
59
  }
44
- /** Scans every `slices/*.md` file (sorted, for deterministic first-wins on an overlapping
45
- * `covers:` claim) and maps each covered slice key to that doc. Directory-scan, not
46
- * the single-file existsSync check every other doc lookup in em uses — the one precedent for
47
- * it is `em ledger`'s (src/cli/ledgerCheck.ts) own `readdirSync(slicesDir)` sweep. */
48
- function readCoverageDocs(slicesDir) {
49
- const coverage = new Map();
50
- if (!existsSync(slicesDir))
51
- return coverage;
52
- const files = readdirSync(slicesDir).filter((f) => f.endsWith(".md")).sort();
53
- for (const file of files) {
54
- const doc = parseSliceDoc(readFileSync(join(slicesDir, file), "utf8"));
55
- for (const coveredKey of doc.covers) {
56
- if (!coverage.has(coveredKey))
57
- coverage.set(coveredKey, doc);
58
- }
59
- }
60
- return coverage;
61
- }
62
60
  //# sourceMappingURL=sliceStatus.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sliceStatus.js","sourceRoot":"","sources":["../../src/render/sliceStatus.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,qEAAqE;AACrE,kFAAkF;AAClF,kFAAkF;AAClF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,+EAA+E;AAC/E,EAAE;AACF,gGAAgG;AAChG,wFAAwF;AACxF,2FAA2F;AAC3F,kGAAkG;AAClG,8FAA8F;AAC9F,qFAAqF;AAErF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAY,MAAM,wBAAwB,CAAC;AAEjE;;wCAEwC;AACxC,MAAM,UAAU,aAAa,CAAC,KAAsB,EAAE,OAAe;IACnE,MAAM,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1C,IAAI,QAAQ,GAAiC,IAAI,CAAC,CAAC,oCAAoC;IAEvF,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QAClD,IAAI,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,aAAa,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7E,QAAQ,KAAK,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACzC,OAAO,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;qEACqE;AACrE,MAAM,UAAU,iBAAiB,CAAC,KAAsB,EAAE,OAAe;IACvE,OAAO,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;AACzE,CAAC;AAED;;;uFAGuF;AACvF,SAAS,gBAAgB,CAAC,SAAiB;IACzC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC7C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5C,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACvE,KAAK,MAAM,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"sliceStatus.js","sourceRoot":"","sources":["../../src/render/sliceStatus.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,qEAAqE;AACrE,kFAAkF;AAClF,kFAAkF;AAClF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,+EAA+E;AAC/E,EAAE;AACF,gGAAgG;AAChG,4FAA4F;AAC5F,+FAA+F;AAC/F,qFAAqF;AACrF,EAAE;AACF,6FAA6F;AAC7F,yFAAyF;AACzF,uFAAuF;AACvF,2FAA2F;AAC3F,SAAS;AAET,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAe,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAY,MAAM,wBAAwB,CAAC;AAEjE;;4EAE4E;AAC5E,MAAM,UAAU,aAAa,CAAC,KAAsB,EAAE,OAAe;IACnE,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1C,IAAI,QAAQ,GAAoC,IAAI,CAAC,CAAC,oCAAoC;IAE1F,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QAClD,IAAI,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,aAAa,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7E,QAAQ,KAAK,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC;QAC7C,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,MAAM,YAAY,GAAG,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACpD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,QAAQ,KAAK,CAAC,CAAC;YACvE,IAAI,UAAU,CAAC,eAAe,CAAC;gBAAE,OAAO,aAAa,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/F,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED;qEACqE;AACrE,MAAM,UAAU,iBAAiB,CAAC,KAAsB,EAAE,OAAe;IACvE,OAAO,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;AACzE,CAAC"}
@@ -1,8 +1,11 @@
1
1
  export declare const STARTER_EM = "model \"Order Fulfillment\"\n\npersona Customer\npersona Manager\n\ncontext Order\ncontext Payment\n\n# --- Command pattern: UI -> command -> event ---\nslice \"Browse Catalog\" {\n ui Product Catalog @Customer\n command Place Order\n event Order Placed @Order\n}\n\n# --- View pattern: event -> read model -> UI ---\nslice \"View Open Orders\" {\n view Open Orders from \"Order Placed\"\n ui Order List @Customer\n}\n\nslice \"Checkout\" {\n ui Checkout Screen @Customer\n command Submit Payment\n event Payment Requested @Payment\n}\n\n# --- View pattern: a human view of pending payments ---\nslice \"Manager Review\" {\n view Pending Payments from \"Payment Requested\"\n ui Payment Dashboard @Manager\n}\n\n# --- Read model the automation watches, in its own slice ---\nslice \"Payments To Process\" {\n view Payments To Process from \"Payment Requested\"\n}\n\n# --- Automation slice: the processor, the command it triggers, and that command's event ---\nslice \"Capture Payment\" {\n processor Payment Gateway from \"Payments To Process\"\n command Capture Payment\n event Payment Captured @Payment\n}\n\nslice \"Show Receipt\" {\n view Receipts from \"Payment Captured\"\n ui Receipt Screen @Customer\n}\n";
2
2
  /** Raw copy of templates/model-readme.md, \`{{...}}\` placeholders intact — see scaffoldReadme(). */
3
- export declare const MODEL_README_TEMPLATE = "# {{Model Name}}\n\n{{One-paragraph description of the business process(es) this event model covers.}}\n\n## Live view\nWhile modeling, run the live view so the team can watch the diagram update:\n\n```bash\nem watch {{model-name}}.em -o {{model-name}}.svg --serve # re-render + instant push-reload\n# then open the URL it prints (http://localhost:5173/?svg={{model-name}}.svg) and share the screen\n```\n\nPan/zoom to navigate the diagram (drag, scroll; **Fit** resets), and click **Review mode** in\nthe header for a slice-by-slice walkthrough. If a save fails to render, the viewer keeps the\nlast good diagram and shows an error banner until the next successful load.\n\nStatic render: `em render {{model-name}}.em -o {{model-name}}.svg`\n\n## Patterns legend\n- **State Change** \u2014 UI \u2192 Command \u2192 Event\n- **State View** \u2014 Event(s) \u2192 Read Model \u2192 UI\n- **Automation** \u2014 Read Model (slice before) \u2192 Processor + Command \u2192 Event, together\n- **Translation** \u2014 External input (or Read Model, slice before) \u2192 Translation + Command \u2192 Event, together\n\nBetween them these are the only legal connections: `ui \u2192 command`, `command \u2192 event`,\n`event \u2192 read model`, `read model \u2192 ui`, `read model \u2192 reaction`, `reaction \u2192 command`. A command\nnever reaches a read model directly \u2014 the event goes between them. Every slice is joined up at\nboth ends: something triggers each command (the screen it's issued from, or the reaction that\ntriggers it, also in this slice), and every event a command records is read by some read model \u2014 so each State\nChange slice is paired with the State View slice that projects its event. A read model repeated\nalong the timeline (`view X again`) shows the same projection at a later point; the instances are\nnever connected to one another.\n\n## Slices\n<!-- The canonical slice index \u2014 the ONE place slices are enumerated (the state file\n points here rather than keeping its own copy). Generated \u2014 run\n `em slice index {{model-name}}.em` to (re)write the table below from the model and its\n slice docs; never hand-edit between the markers. -->\n<!-- GENERATED:slices:start -->\n| # | Slice | Pattern | Status | Ratified by | Owner | Tracking | Implemented in | Design doc |\n|---|-------|---------|--------|-------------|-------|----------|----------------|------------|\n<!-- GENERATED:slices:end -->\n\n## Status\nSee [`.event-modeling.md`](.event-modeling.md) for current phase, decisions, and open questions.\n";
3
+ export declare const MODEL_README_TEMPLATE = "# {{Model Name}}\n\n{{One-paragraph description of the business process(es) this event model covers.}}\n\n## Live view\nWhile modeling, run the live view so the team can watch the diagram update:\n\n```bash\nem watch {{model-name}}.em -o {{model-name}}.svg --serve # re-render + instant push-reload\n# then open the URL it prints (http://localhost:5173/?svg={{model-name}}.svg) and share the screen\n```\n\nPan/zoom to navigate the diagram (drag, scroll; **Fit** resets), and click **Review mode** in\nthe header for a slice-by-slice walkthrough. If a save fails to render, the viewer keeps the\nlast good diagram and shows an error banner until the next successful load.\n\nStatic render: `em render {{model-name}}.em -o {{model-name}}.svg`\n\n## Patterns legend\n- **State Change** \u2014 UI \u2192 Command \u2192 Event\n- **State View** \u2014 Event(s) \u2192 Read Model \u2192 UI\n- **Automation** \u2014 Read Model (slice before) \u2192 Processor + Command \u2192 Event, together\n- **Translation** \u2014 External input (or Read Model, slice before) \u2192 Translation + Command \u2192 Event, together\n\nBetween them these are the only legal connections: `ui \u2192 command`, `command \u2192 event`,\n`event \u2192 read model`, `read model \u2192 ui`, `read model \u2192 reaction`, `reaction \u2192 command`. A command\nnever reaches a read model directly \u2014 the event goes between them. Every slice is joined up at\nboth ends: something triggers each command (the screen it's issued from, or the reaction that\ntriggers it, also in this slice), and every event a command records is read by some read model \u2014 so each State\nChange slice is paired with the State View slice that projects its event. A read model repeated\nalong the timeline (`view X again`) shows the same projection at a later point; the instances are\nnever connected to one another.\n\n## Slices\n<!-- The canonical slice index \u2014 the ONE place slices are enumerated (the state file\n points here rather than keeping its own copy). Generated \u2014 run\n `em slice index {{model-name}}.em` to (re)write the table below from the model and its\n slice docs; never hand-edit between the markers. -->\n<!-- GENERATED:slices:start -->\n| # | Slice | Pattern | Status | Reviewed by | Ratified by | Owner | Tracking | Implemented in | Design doc |\n|---|-------|---------|--------|-------------|-------------|-------|----------|----------------|------------|\n<!-- GENERATED:slices:end -->\n\n## Status\nSee [`.event-modeling.md`](.event-modeling.md) for current phase, decisions, and open questions.\n";
4
4
  /** Raw copy of templates/state.md, \`{{...}}\` placeholders intact — see scaffoldStateFile(). */
5
5
  export declare const STATE_TEMPLATE = "<!--\nResumable progress state for an event model. Stored as <model>/.event-modeling.md\nThe skill reads this on `/event-modeling` (no arg) to resume where you left off.\nKeep it current at the end of every working session.\n-->\n\n# Event Modeling Progress \u2014 {{Model Name}}\n\n- **Model file:** `{{model-name}}.em`\n- **Current phase:** {{discover | extract | model | slice | implement | conform | review | validate}}\n- **Current step:** {{1\u20137, see methodology; or extraction round R1\u2013R7}}\n- **Last updated:** {{YYYY-MM-DD}}\n- **Last conformance:** {{YYYY-MM-DD @ <target-repo revision> \u2014 report: conformance/<date>-report.md | never}}\n- **Last stakeholder review:** {{YYYY-MM-DD \u2014 attendees: see Participants | never}}\n\n## Session inputs\n- **Scope line:** {{one-line description of what's in/out of bounds for this model}}\n- **PRD / spec reference:** {{path, link, or \"none\"}}\n- **Headless/API model:** {{yes | no}}\n- **Source mode:** {{greenfield | extract-event-driven | extract-procedural}}\n- **Existing system refs:** {{repo paths, event-schema/topic locations, docs \u2014 or \"n/a\"}}\n\n## Participants\n<!-- Populate at session start. Live workshop: one human proxy relays questions to the room;\n attribute every answer/decision in the Decisions log to a named participant here. -->\n- {{Name}} \u2014 {{role}} \u2014 {{domain area}}\n\n## Extraction progress (existing-system models only \u2014 delete for greenfield)\n- [ ] R1. Candidate events (extracted/synthesized, filtered, confirmed)\n- [ ] R2. Timeline order (as-is narrative, actors/callers, first render)\n- [ ] R3. Commands / inputs\n- [ ] R4. Read models / outputs (validate from here)\n- [ ] R5. Boundaries & reactions (reaction shares its slice with the command it triggers)\n- [ ] R6. Gap & TBD reconciliation\n- [ ] R7. Convergence (render + validate clean, user confirmed as-is)\n\n## Steps completed\n- [ ] 1. Brainstorm events\n- [ ] 2. Plot / storyboard (personas + UI)\n- [ ] 3. Inputs (commands)\n- [ ] 4. Outputs (read models)\n- [ ] 5. Swimlanes & apply patterns\n- [ ] 6. Elaborate scenarios\n- [ ] 7. Evaluate completeness (`em validate` clean)\n\n## Decisions log\n<!-- Resolved choices, with the reasoning, so they aren't re-litigated. In a live workshop,\n attribute each entry to the participant who made the call (see Participants above). -->\n- {{YYYY-MM-DD}}: {{decision}} \u2014 {{why}} \u2014 by {{participant, if a live workshop}}\n\n## Usage log\n<!-- The team's only usage signal today (see docs/usage-data.md) \u2014 cheap and coarse on purpose.\n One line per session: phase(s) touched, and validate diagnostic *categories* hit (one of\n the exact fixed strings in docs/usage-data.md#categories, e.g. \"read model has no\n consumer\" \u2014 never the full instance message or any domain content). Append, never edit\n past entries. -->\n- {{YYYY-MM-DD}}: phases: {{discover | extract | model | slice | implement | conform | review | validate | watch, ...}} \u2014 validate: {{diagnostic category (docs/usage-data.md#categories), ... | none}}\n\n## Open questions / parking lot\n<!-- Unresolved items to bring back to the user. Never guess these. Metadata is optional and\n flat \u2014 add only what's known: source (ticket/conversation that spawned it), blocked on\n (who/what), revisit (when). -->\n- [ ] {{question}} \u2014 source: {{ticket/conversation link, optional}} \u2014 blocked on: {{who/what, optional}} \u2014 revisit: {{when, optional}}\n\n## Slice inventory\n<!-- Deliberately NOT a table: the canonical slice index (slice, pattern, status, doc link)\n lives in README.md's \"Slices\" section \u2014 one source of truth, updated there. This file\n only tracks resumable session state. -->\nSee `README.md` \u2192 **Slices** for the slice index and per-slice doc status.\n";
6
+ /** Raw copy of templates/constitution.md, \`{{...}}\` placeholders intact — see
7
+ * scaffoldConstitution(). */
8
+ export declare const CONSTITUTION_TEMPLATE = "<!--\nThe implementation constitution: the house rules an implementing agent follows on EVERY slice \u2014\nstack, code style, testing norms, NFR baselines, review norms. It answers \"how do we build things\nhere\" once, so no slice doc has to repeat it and no agent has to invent it. Slice docs say WHAT to\nbuild; this says HOW it gets built here.\n\nWhere this document lives:\n - No SDD tool: `constitution.md` beside the model \u2014 `em scaffold` writes it there.\n - spec-kit project (a `.specify/` directory exists anywhere above the model): the document IS\n `.specify/memory/constitution.md`. em writes no second copy; the sections below merge into\n that file under these same headings, never overwriting text already there, and ratification is\n recorded in spec-kit's own `**Ratified**:` footer line instead of the frontmatter below.\n\nHow it gets filled: not by an agent guessing. Each section opens with the question a short\nfacilitated conversation asks before the first slice is implemented \u2014 the procedure is \"Before the\nfirst slice: the constitution\" in the `event-modeling-implement` skill's `reference/implement.md`\n(`em contract` prints it). Replace each `{{...}}` block with the real answers; delete the guidance\ncomments and any row that doesn't apply.\n\nRatification: `ratifiedBy:`/`ratifiedOn:` are the same identity discipline slice docs use \u2014 a\nNAMED human and an ISO date, hand-filled at sign-off (no `em` command writes this file).\n**A constitution with an empty `ratifiedBy:` is a draft**: an implementing agent treats it as\nunratified, says so, and does not proceed on unilateral style or stack decisions for a first slice.\n\n`em status` reports this document as present or absent per model \u2014 existence only. em never reads\nor validates its content, and nothing here is machine-parsed.\n-->\n\n---\nschemaVersion: 1\nratifiedBy:\nratifiedOn:\n---\n# {{Project Name}} \u2014 implementation constitution\n\nThe house rules for how implementation happens in this project. They bound every \"purely\ntechnical choice\" an implementing agent would otherwise make alone.\n\n## Stack and architectural shape\n\n> **What do we build on, what shape does the code take, and which implementation skill or\n> approach does each slice pattern route to?**\n\n- **Languages / runtimes:** {{e.g. Kotlin 2.x on JVM 21; TypeScript 5 on Node 24}}\n- **Frameworks / platform:** {{web framework, persistence, messaging, build tool}}\n- **Architectural pattern:** {{e.g. event-sourced CQRS with vertical slices; layered service over\n a relational store; functional core / imperative shell}}\n- **Where a slice's code lives:** {{one package per slice, per context, ...}}\n- **Interface conventions:** {{resource style \u2014 REST/RPC/GraphQL, etc.; error/rejection mapping;\n versioning; auth at the edge \u2014 the shape every `ui`-triggered command or `ui`-consumed view's\n endpoint follows, decided once here rather than invented per slice}}\n\nRouting \u2014 which implementation skill or approach each of the four slice patterns uses:\n\n| Pattern | Skill / approach |\n|---|---|\n| State Change | {{e.g. the `axon-*` skill set \u2014 axon-entity-patterns, then axon-entity-testing}} |\n| State View | {{e.g. the `axon-*` skill set \u2014 axon-projection-patterns, then axon-projection-testing}} |\n| Automation | {{e.g. the `axon-*` skill set \u2014 axon-policy-patterns, then axon-policy-testing}} |\n| Translation | {{e.g. the `axon-*` skill set \u2014 axon-translation-patterns}} |\n\nAn agent implementing a slice reads the doc's `pattern:` frontmatter and follows that row \u2014 this\ntable is the only routing rule.\n\n## Code style\n\n> **What does \"idiomatic\" mean in this codebase \u2014 formatting, naming, module layout?**\n\n- **Formatter / linter:** {{the tool and the command that runs it \u2014 never hand-format against it}}\n- **Naming:** {{commands, events, handlers, files, test names}}\n- **Module layout:** {{what lives in which file/package, and what may not import what}}\n- **Idiomatic here means:** {{the two or three habits a new contributor most often gets wrong}}\n\n## Testing norms\n\n> **What must be tested for each slice pattern, where do those tests live, and how are they named\n> so `em coverage` can trace invariant IDs?**\n\n- **Levels required per pattern:** {{e.g. State Change \u2014 decider unit tests plus a\n given/when/then fixture test; State View \u2014 projection tests; Automation / Translation \u2014\n reaction tests, including a negative case}}\n- **Invariant tests:** {{where they live}} \u2014 every `INV-<MNEMONIC>-n` in a slice doc needs at least\n one test **citing that exact ID** in its name or a comment, so\n `em coverage <model>.em --tests <dir>` can trace it.\n- **Scenario tests:** {{where they live}} \u2014 one per `## Scenarios (Given / When / Then)` entry,\n with rejection scenarios asserting the doc's named rejection reason.\n- **Test naming convention:** {{the exact pattern, e.g. `INV-ORD-3: rejects a second submit`}}\n- **What \"green\" means:** {{the command CI runs}}\n\n## NFR baselines\n\n> **What standing non-functional defaults does every slice inherit \u2014 authz, PII, performance,\n> observability?**\n\n- **Authorization model:** {{who may issue which commands, how it's enforced, and where}}\n- **PII / data handling:** {{what is sensitive, what may be logged, retention}}\n- **Performance defaults:** {{latency / throughput budgets that apply unless a slice says otherwise}}\n- **Observability:** {{logging, metrics, tracing every slice is expected to emit}}\n\nA slice doc's own `## Non-Functional Requirements` section overrides these for that slice; where\nit is silent, these apply.\n\n## Review and merge norms\n\n> **Who reviews an implementation PR, what blocks a merge, and how does the slice doc's status\n> flip fit in?**\n\n- **Reviewers:** {{who \u2014 a team, a CODEOWNERS rule, the slice's ratifier}}\n- **What blocks a merge:** {{e.g. a failing suite, an uncovered invariant, a gap that belongs back\n at the model}}\n- **Branch / commit conventions:** {{...}} \u2014 name only branch naming and commit-message style\n here. **One slice doc = one PR is the implement contract's rule, not this document's to\n amend** \u2014 a `covers:` doc ships as one PR for both keys; a read model is one doc, so a later\n `again` instance is a continuation of the doc that first declared it, not a PR of its own. See\n the agent guide's \u00A75/\u00A710.\n- **At merge:** run `em slice mark-implemented <model>.em <slice-key> <pr-url>` \u2014 the one edit an\n implementing agent makes to a ratified slice doc, run by {{the PR author | CI}}. Never bump\n `version:`; that moves only when a delta is ratified.\n";
6
9
  /** Same STARTER_EM content `em init` writes, titled from an arbitrary display name — used by
7
10
  * `em scaffold`, which (unlike `em init`) knows a name to title the model with. */
8
11
  export declare function starterEmFor(title: string): string;
@@ -18,3 +21,13 @@ export declare function scaffoldReadme(displayName: string, slugName: string): s
18
21
  * the template's own guidance comment intact where it has one — per the skill's "don't guess,
19
22
  * park it" principle, never a fabricated example. Never leaves \`{{...}}\` in the result. */
20
23
  export declare function scaffoldStateFile(displayName: string, slugName: string, today: string): string;
24
+ /** Fill CONSTITUTION_TEMPLATE's project-name placeholder for `em scaffold` (MIL-202).
25
+ *
26
+ * Unlike `scaffoldReadme`/`scaffoldStateFile`, this one **deliberately leaves every other
27
+ * \`{{...}}\` block intact**: they are the unanswered questions of the elicitation conversation
28
+ * the implement skill runs before the first slice ("Before the first slice: the constitution"),
29
+ * not mechanical fields em could fill. An unanswered constitution must read as unanswered — the
30
+ * placeholders are the todo list, and the empty `ratifiedBy:` frontmatter says it's still a
31
+ * draft. Nothing in `em` ever parses this file's body; only its existence is reported
32
+ * (`em status`'s `constitution` field). */
33
+ export declare function scaffoldConstitution(displayName: string): string;
package/dist/templates.js CHANGED
@@ -56,9 +56,9 @@ slice "Show Receipt" {
56
56
  // mirroring STARTER_EM above — the compiled CLI never reads .claude/skills/ at runtime to
57
57
  // produce these, so `em scaffold` behaves identically whether em is run from a checkout or
58
58
  // installed from npm. test/scaffoldTemplatesSync.test.ts asserts each constant below stays
59
- // byte-identical to its source file in .claude/skills/event-modeling/templates/, so the two
60
- // copies can't silently drift — regenerate by hand (or re-run the one-off script noted there)
61
- // whenever a template file changes.
59
+ // byte-identical to its source file in .claude/skills/event-modeling-shared/templates/, so the
60
+ // two copies can't silently drift — regenerate by hand (or re-run the one-off script noted
61
+ // there) whenever a template file changes.
62
62
  /** Raw copy of templates/model-readme.md, \`{{...}}\` placeholders intact — see scaffoldReadme(). */
63
63
  export const MODEL_README_TEMPLATE = `# {{Model Name}}
64
64
 
@@ -99,8 +99,8 @@ never connected to one another.
99
99
  \`em slice index {{model-name}}.em\` to (re)write the table below from the model and its
100
100
  slice docs; never hand-edit between the markers. -->
101
101
  <!-- GENERATED:slices:start -->
102
- | # | Slice | Pattern | Status | Ratified by | Owner | Tracking | Implemented in | Design doc |
103
- |---|-------|---------|--------|-------------|-------|----------|----------------|------------|
102
+ | # | Slice | Pattern | Status | Reviewed by | Ratified by | Owner | Tracking | Implemented in | Design doc |
103
+ |---|-------|---------|--------|-------------|-------------|-------|----------|----------------|------------|
104
104
  <!-- GENERATED:slices:end -->
105
105
 
106
106
  ## Status
@@ -177,6 +177,127 @@ Keep it current at the end of every working session.
177
177
  only tracks resumable session state. -->
178
178
  See \`README.md\` → **Slices** for the slice index and per-slice doc status.
179
179
  `;
180
+ /** Raw copy of templates/constitution.md, \`{{...}}\` placeholders intact — see
181
+ * scaffoldConstitution(). */
182
+ export const CONSTITUTION_TEMPLATE = `<!--
183
+ The implementation constitution: the house rules an implementing agent follows on EVERY slice —
184
+ stack, code style, testing norms, NFR baselines, review norms. It answers "how do we build things
185
+ here" once, so no slice doc has to repeat it and no agent has to invent it. Slice docs say WHAT to
186
+ build; this says HOW it gets built here.
187
+
188
+ Where this document lives:
189
+ - No SDD tool: \`constitution.md\` beside the model — \`em scaffold\` writes it there.
190
+ - spec-kit project (a \`.specify/\` directory exists anywhere above the model): the document IS
191
+ \`.specify/memory/constitution.md\`. em writes no second copy; the sections below merge into
192
+ that file under these same headings, never overwriting text already there, and ratification is
193
+ recorded in spec-kit's own \`**Ratified**:\` footer line instead of the frontmatter below.
194
+
195
+ How it gets filled: not by an agent guessing. Each section opens with the question a short
196
+ facilitated conversation asks before the first slice is implemented — the procedure is "Before the
197
+ first slice: the constitution" in the \`event-modeling-implement\` skill's \`reference/implement.md\`
198
+ (\`em contract\` prints it). Replace each \`{{...}}\` block with the real answers; delete the guidance
199
+ comments and any row that doesn't apply.
200
+
201
+ Ratification: \`ratifiedBy:\`/\`ratifiedOn:\` are the same identity discipline slice docs use — a
202
+ NAMED human and an ISO date, hand-filled at sign-off (no \`em\` command writes this file).
203
+ **A constitution with an empty \`ratifiedBy:\` is a draft**: an implementing agent treats it as
204
+ unratified, says so, and does not proceed on unilateral style or stack decisions for a first slice.
205
+
206
+ \`em status\` reports this document as present or absent per model — existence only. em never reads
207
+ or validates its content, and nothing here is machine-parsed.
208
+ -->
209
+
210
+ ---
211
+ schemaVersion: 1
212
+ ratifiedBy:
213
+ ratifiedOn:
214
+ ---
215
+ # {{Project Name}} — implementation constitution
216
+
217
+ The house rules for how implementation happens in this project. They bound every "purely
218
+ technical choice" an implementing agent would otherwise make alone.
219
+
220
+ ## Stack and architectural shape
221
+
222
+ > **What do we build on, what shape does the code take, and which implementation skill or
223
+ > approach does each slice pattern route to?**
224
+
225
+ - **Languages / runtimes:** {{e.g. Kotlin 2.x on JVM 21; TypeScript 5 on Node 24}}
226
+ - **Frameworks / platform:** {{web framework, persistence, messaging, build tool}}
227
+ - **Architectural pattern:** {{e.g. event-sourced CQRS with vertical slices; layered service over
228
+ a relational store; functional core / imperative shell}}
229
+ - **Where a slice's code lives:** {{one package per slice, per context, ...}}
230
+ - **Interface conventions:** {{resource style — REST/RPC/GraphQL, etc.; error/rejection mapping;
231
+ versioning; auth at the edge — the shape every \`ui\`-triggered command or \`ui\`-consumed view's
232
+ endpoint follows, decided once here rather than invented per slice}}
233
+
234
+ Routing — which implementation skill or approach each of the four slice patterns uses:
235
+
236
+ | Pattern | Skill / approach |
237
+ |---|---|
238
+ | State Change | {{e.g. the \`axon-*\` skill set — axon-entity-patterns, then axon-entity-testing}} |
239
+ | State View | {{e.g. the \`axon-*\` skill set — axon-projection-patterns, then axon-projection-testing}} |
240
+ | Automation | {{e.g. the \`axon-*\` skill set — axon-policy-patterns, then axon-policy-testing}} |
241
+ | Translation | {{e.g. the \`axon-*\` skill set — axon-translation-patterns}} |
242
+
243
+ An agent implementing a slice reads the doc's \`pattern:\` frontmatter and follows that row — this
244
+ table is the only routing rule.
245
+
246
+ ## Code style
247
+
248
+ > **What does "idiomatic" mean in this codebase — formatting, naming, module layout?**
249
+
250
+ - **Formatter / linter:** {{the tool and the command that runs it — never hand-format against it}}
251
+ - **Naming:** {{commands, events, handlers, files, test names}}
252
+ - **Module layout:** {{what lives in which file/package, and what may not import what}}
253
+ - **Idiomatic here means:** {{the two or three habits a new contributor most often gets wrong}}
254
+
255
+ ## Testing norms
256
+
257
+ > **What must be tested for each slice pattern, where do those tests live, and how are they named
258
+ > so \`em coverage\` can trace invariant IDs?**
259
+
260
+ - **Levels required per pattern:** {{e.g. State Change — decider unit tests plus a
261
+ given/when/then fixture test; State View — projection tests; Automation / Translation —
262
+ reaction tests, including a negative case}}
263
+ - **Invariant tests:** {{where they live}} — every \`INV-<MNEMONIC>-n\` in a slice doc needs at least
264
+ one test **citing that exact ID** in its name or a comment, so
265
+ \`em coverage <model>.em --tests <dir>\` can trace it.
266
+ - **Scenario tests:** {{where they live}} — one per \`## Scenarios (Given / When / Then)\` entry,
267
+ with rejection scenarios asserting the doc's named rejection reason.
268
+ - **Test naming convention:** {{the exact pattern, e.g. \`INV-ORD-3: rejects a second submit\`}}
269
+ - **What "green" means:** {{the command CI runs}}
270
+
271
+ ## NFR baselines
272
+
273
+ > **What standing non-functional defaults does every slice inherit — authz, PII, performance,
274
+ > observability?**
275
+
276
+ - **Authorization model:** {{who may issue which commands, how it's enforced, and where}}
277
+ - **PII / data handling:** {{what is sensitive, what may be logged, retention}}
278
+ - **Performance defaults:** {{latency / throughput budgets that apply unless a slice says otherwise}}
279
+ - **Observability:** {{logging, metrics, tracing every slice is expected to emit}}
280
+
281
+ A slice doc's own \`## Non-Functional Requirements\` section overrides these for that slice; where
282
+ it is silent, these apply.
283
+
284
+ ## Review and merge norms
285
+
286
+ > **Who reviews an implementation PR, what blocks a merge, and how does the slice doc's status
287
+ > flip fit in?**
288
+
289
+ - **Reviewers:** {{who — a team, a CODEOWNERS rule, the slice's ratifier}}
290
+ - **What blocks a merge:** {{e.g. a failing suite, an uncovered invariant, a gap that belongs back
291
+ at the model}}
292
+ - **Branch / commit conventions:** {{...}} — name only branch naming and commit-message style
293
+ here. **One slice doc = one PR is the implement contract's rule, not this document's to
294
+ amend** — a \`covers:\` doc ships as one PR for both keys; a read model is one doc, so a later
295
+ \`again\` instance is a continuation of the doc that first declared it, not a PR of its own. See
296
+ the agent guide's §5/§10.
297
+ - **At merge:** run \`em slice mark-implemented <model>.em <slice-key> <pr-url>\` — the one edit an
298
+ implementing agent makes to a ratified slice doc, run by {{the PR author | CI}}. Never bump
299
+ \`version:\`; that moves only when a delta is ratified.
300
+ `;
180
301
  /** Same STARTER_EM content `em init` writes, titled from an arbitrary display name — used by
181
302
  * `em scaffold`, which (unlike `em init`) knows a name to title the model with. */
182
303
  export function starterEmFor(title) {
@@ -219,4 +340,19 @@ export function scaffoldStateFile(displayName, slugName, today) {
219
340
  .filter((line) => !line.includes("{{"))
220
341
  .join("\n");
221
342
  }
343
+ /** Fill CONSTITUTION_TEMPLATE's project-name placeholder for `em scaffold` (MIL-202).
344
+ *
345
+ * Unlike `scaffoldReadme`/`scaffoldStateFile`, this one **deliberately leaves every other
346
+ * \`{{...}}\` block intact**: they are the unanswered questions of the elicitation conversation
347
+ * the implement skill runs before the first slice ("Before the first slice: the constitution"),
348
+ * not mechanical fields em could fill. An unanswered constitution must read as unanswered — the
349
+ * placeholders are the todo list, and the empty `ratifiedBy:` frontmatter says it's still a
350
+ * draft. Nothing in `em` ever parses this file's body; only its existence is reported
351
+ * (`em status`'s `constitution` field). */
352
+ export function scaffoldConstitution(displayName) {
353
+ // Replacer function, not a string pattern — a string replacement would let `$&`/`$$`/etc. in
354
+ // displayName expand against the match instead of being inserted literally (the CLI layer
355
+ // separately rejects `{{` in the name).
356
+ return CONSTITUTION_TEMPLATE.replace("{{Project Name}}", () => displayName);
357
+ }
222
358
  //# sourceMappingURL=templates.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,mCAAmC;AAEnC,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiDzB,CAAC;AAEF,wDAAwD;AACxD,EAAE;AACF,2FAA2F;AAC3F,0FAA0F;AAC1F,2FAA2F;AAC3F,2FAA2F;AAC3F,4FAA4F;AAC5F,8FAA8F;AAC9F,oCAAoC;AAEpC,qGAAqG;AACrG,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CpC,CAAC;AAEF,iGAAiG;AACjG,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqE7B,CAAC;AAEF;oFACoF;AACpF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,0FAA0F;IAC1F,yFAAyF;IACzF,6FAA6F;IAC7F,OAAO,UAAU,CAAC,OAAO,CAAC,2BAA2B,EAAE,GAAG,EAAE,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC;AACnF,CAAC;AAED;;;;4DAI4D;AAC5D,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,QAAgB;IAClE,6FAA6F;IAC7F,4EAA4E;IAC5E,OAAO,qBAAqB,CAAC,OAAO,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC;SAC3E,OAAO,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC;SAC9C,OAAO,CACN,oFAAoF,EACpF,yFAAyF,CAC1F,CAAC;AACN,CAAC;AAED;;;;8FAI8F;AAC9F,MAAM,UAAU,iBAAiB,CAAC,WAAmB,EAAE,QAAgB,EAAE,KAAa;IACpF,qFAAqF;IACrF,wFAAwF;IACxF,4BAA4B;IAC5B,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC;SACvE,OAAO,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC;SACzC,OAAO,CAAC,kFAAkF,EAAE,UAAU,CAAC;SACvG,OAAO,CAAC,+DAA+D,EAAE,GAAG,CAAC;SAC7E,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC,8DAA8D;QAChG,oEAAoE;SACnE,OAAO,CAAC,6FAA6F,EAAE,OAAO,CAAC;SAC/G,OAAO,CAAC,2DAA2D,EAAE,OAAO,CAAC,CAAC;IAEjF,OAAO,MAAM;SACV,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;SACtC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,mCAAmC;AAEnC,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiDzB,CAAC;AAEF,wDAAwD;AACxD,EAAE;AACF,2FAA2F;AAC3F,0FAA0F;AAC1F,2FAA2F;AAC3F,2FAA2F;AAC3F,+FAA+F;AAC/F,2FAA2F;AAC3F,2CAA2C;AAE3C,qGAAqG;AACrG,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CpC,CAAC;AAEF,iGAAiG;AACjG,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqE7B,CAAC;AAEF;8BAC8B;AAC9B,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsHpC,CAAC;AAEF;oFACoF;AACpF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,0FAA0F;IAC1F,yFAAyF;IACzF,6FAA6F;IAC7F,OAAO,UAAU,CAAC,OAAO,CAAC,2BAA2B,EAAE,GAAG,EAAE,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC;AACnF,CAAC;AAED;;;;4DAI4D;AAC5D,MAAM,UAAU,cAAc,CAAC,WAAmB,EAAE,QAAgB;IAClE,6FAA6F;IAC7F,4EAA4E;IAC5E,OAAO,qBAAqB,CAAC,OAAO,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC;SAC3E,OAAO,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC;SAC9C,OAAO,CACN,oFAAoF,EACpF,yFAAyF,CAC1F,CAAC;AACN,CAAC;AAED;;;;8FAI8F;AAC9F,MAAM,UAAU,iBAAiB,CAAC,WAAmB,EAAE,QAAgB,EAAE,KAAa;IACpF,qFAAqF;IACrF,wFAAwF;IACxF,4BAA4B;IAC5B,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC;SACvE,OAAO,CAAC,gBAAgB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC;SACzC,OAAO,CAAC,kFAAkF,EAAE,UAAU,CAAC;SACvG,OAAO,CAAC,+DAA+D,EAAE,GAAG,CAAC;SAC7E,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC,8DAA8D;QAChG,oEAAoE;SACnE,OAAO,CAAC,6FAA6F,EAAE,OAAO,CAAC;SAC/G,OAAO,CAAC,2DAA2D,EAAE,OAAO,CAAC,CAAC;IAEjF,OAAO,MAAM;SACV,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;SACtC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;;;;;4CAQ4C;AAC5C,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,6FAA6F;IAC7F,0FAA0F;IAC1F,wCAAwC;IACxC,OAAO,qBAAqB,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** `YYYY-MM-DD` for `d`'s local calendar date (default: now). Zero-pads month/day. */
2
+ export declare function localIsoDate(d?: Date): string;
@@ -0,0 +1,17 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Shared "what calendar date is it" helper for every date-defaulting command (MIL-206).
3
+ //
4
+ // `new Date().toISOString().slice(0, 10)` reads the UTC calendar date, not the caller's. After
5
+ // ~17:00 in the Americas (any zone west of UTC), that's already tomorrow: someone ratifying a
6
+ // slice at 6pm local time on the 6th got `ratifiedOn: 2026-09-07` stamped instead. Build the
7
+ // string from the local getters instead, so the default always matches what the caller's own
8
+ // clock and calendar say "today" is. A caller who wants UTC (or any other zone) can still pass
9
+ // an explicit date through `--on`.
10
+ /** `YYYY-MM-DD` for `d`'s local calendar date (default: now). Zero-pads month/day. */
11
+ export function localIsoDate(d = new Date()) {
12
+ const year = d.getFullYear();
13
+ const month = String(d.getMonth() + 1).padStart(2, "0");
14
+ const day = String(d.getDate()).padStart(2, "0");
15
+ return `${year}-${month}-${day}`;
16
+ }
17
+ //# sourceMappingURL=localDate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localDate.js","sourceRoot":"","sources":["../../src/util/localDate.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,wFAAwF;AACxF,EAAE;AACF,+FAA+F;AAC/F,8FAA8F;AAC9F,6FAA6F;AAC7F,6FAA6F;AAC7F,+FAA+F;AAC/F,mCAAmC;AAEnC,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,IAAU,IAAI,IAAI,EAAE;IAC/C,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;AACnC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milehimikey/em",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "AI-friendly Event Modeling — a slice-first text DSL rendered to a strict, self-contained Graphviz grid (SVG/PNG)",
5
5
  "type": "module",
6
6
  "bin": {