@milehimikey/em 1.9.1 → 1.11.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 (125) 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 +31 -12
  4. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  5. package/.claude/skills/event-modeling-implement/SKILL.md +14 -3
  6. package/.claude/skills/event-modeling-implement/reference/implement.md +73 -6
  7. package/.claude/skills/event-modeling-review/SKILL.md +14 -3
  8. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +110 -11
  9. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +4 -1
  10. package/.claude/skills/event-modeling-shared/templates/constitution.md +111 -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/README.md +8 -1
  14. package/dist/catalog/docJoin.d.ts +8 -0
  15. package/dist/catalog/docJoin.js +4 -0
  16. package/dist/catalog/docJoin.js.map +1 -1
  17. package/dist/catalog/sliceDoc.d.ts +8 -0
  18. package/dist/catalog/sliceDoc.js +2 -0
  19. package/dist/catalog/sliceDoc.js.map +1 -1
  20. package/dist/cli/ciInit.d.ts +10 -3
  21. package/dist/cli/ciInit.js +22 -14
  22. package/dist/cli/ciInit.js.map +1 -1
  23. package/dist/cli/ledgerCheck.d.ts +72 -0
  24. package/dist/cli/ledgerCheck.js +122 -1
  25. package/dist/cli/ledgerCheck.js.map +1 -1
  26. package/dist/cli/ratify.d.ts +37 -5
  27. package/dist/cli/ratify.js +106 -9
  28. package/dist/cli/ratify.js.map +1 -1
  29. package/dist/cli/reratify.js +14 -5
  30. package/dist/cli/reratify.js.map +1 -1
  31. package/dist/cli/review.d.ts +38 -0
  32. package/dist/cli/review.js +160 -0
  33. package/dist/cli/review.js.map +1 -0
  34. package/dist/cli/sliceIndex.d.ts +3 -0
  35. package/dist/cli/sliceIndex.js +5 -4
  36. package/dist/cli/sliceIndex.js.map +1 -1
  37. package/dist/cli/stateFile.d.ts +10 -0
  38. package/dist/cli/stateFile.js +14 -0
  39. package/dist/cli/stateFile.js.map +1 -1
  40. package/dist/cli/status.d.ts +44 -3
  41. package/dist/cli/status.js +75 -18
  42. package/dist/cli/status.js.map +1 -1
  43. package/dist/cli/systemInputs.d.ts +34 -0
  44. package/dist/cli/systemInputs.js +129 -0
  45. package/dist/cli/systemInputs.js.map +1 -0
  46. package/dist/cli.js +362 -23
  47. package/dist/cli.js.map +1 -1
  48. package/dist/emit/freshnessJson.d.ts +1 -1
  49. package/dist/emit/freshnessJson.js +5 -1
  50. package/dist/emit/freshnessJson.js.map +1 -1
  51. package/dist/emit/json.d.ts +37 -1
  52. package/dist/emit/json.js +67 -1
  53. package/dist/emit/json.js.map +1 -1
  54. package/dist/emit/ledgerJson.d.ts +1 -1
  55. package/dist/emit/ledgerJson.js +6 -1
  56. package/dist/emit/ledgerJson.js.map +1 -1
  57. package/dist/emit/queryJson.d.ts +6 -0
  58. package/dist/emit/queryJson.js +33 -0
  59. package/dist/emit/queryJson.js.map +1 -0
  60. package/dist/emit/statusJson.d.ts +1 -1
  61. package/dist/emit/statusJson.js +5 -1
  62. package/dist/emit/statusJson.js.map +1 -1
  63. package/dist/emit/systemJson.d.ts +8 -0
  64. package/dist/emit/systemJson.js +38 -0
  65. package/dist/emit/systemJson.js.map +1 -0
  66. package/dist/mcp/server.d.ts +1 -1
  67. package/dist/mcp/server.js +195 -9
  68. package/dist/mcp/server.js.map +1 -1
  69. package/dist/model/edges.d.ts +28 -1
  70. package/dist/model/edges.js +77 -15
  71. package/dist/model/edges.js.map +1 -1
  72. package/dist/model/model.d.ts +6 -0
  73. package/dist/model/model.js +2 -0
  74. package/dist/model/model.js.map +1 -1
  75. package/dist/model/qualifiedRef.d.ts +47 -0
  76. package/dist/model/qualifiedRef.js +100 -0
  77. package/dist/model/qualifiedRef.js.map +1 -0
  78. package/dist/model/queryIndex.d.ts +53 -0
  79. package/dist/model/queryIndex.js +114 -0
  80. package/dist/model/queryIndex.js.map +1 -0
  81. package/dist/model/rules.d.ts +88 -0
  82. package/dist/model/rules.js +101 -0
  83. package/dist/model/rules.js.map +1 -1
  84. package/dist/model/sliceDocIndex.d.ts +30 -0
  85. package/dist/model/sliceDocIndex.js +110 -0
  86. package/dist/model/sliceDocIndex.js.map +1 -0
  87. package/dist/model/validate.js +85 -11
  88. package/dist/model/validate.js.map +1 -1
  89. package/dist/parser/ast.d.ts +25 -0
  90. package/dist/parser/parser.d.ts +2 -0
  91. package/dist/parser/parser.js +104 -34
  92. package/dist/parser/parser.js.map +1 -1
  93. package/dist/query/format.d.ts +8 -0
  94. package/dist/query/format.js +80 -0
  95. package/dist/query/format.js.map +1 -0
  96. package/dist/query/pipeline.d.ts +17 -0
  97. package/dist/query/pipeline.js +38 -0
  98. package/dist/query/pipeline.js.map +1 -0
  99. package/dist/query/system.d.ts +60 -0
  100. package/dist/query/system.js +87 -0
  101. package/dist/query/system.js.map +1 -0
  102. package/dist/query/verbs.d.ts +100 -0
  103. package/dist/query/verbs.js +306 -0
  104. package/dist/query/verbs.js.map +1 -0
  105. package/dist/render/drawEdges.js +15 -4
  106. package/dist/render/drawEdges.js.map +1 -1
  107. package/dist/render/drawNotes.d.ts +13 -3
  108. package/dist/render/drawNotes.js +96 -20
  109. package/dist/render/drawNotes.js.map +1 -1
  110. package/dist/render/render.js +6 -5
  111. package/dist/render/render.js.map +1 -1
  112. package/dist/render/sliceDiagram.js +1 -0
  113. package/dist/render/sliceDiagram.js.map +1 -1
  114. package/dist/system/manifest.d.ts +45 -0
  115. package/dist/system/manifest.js +178 -0
  116. package/dist/system/manifest.js.map +1 -0
  117. package/dist/system/verify.d.ts +88 -0
  118. package/dist/system/verify.js +263 -0
  119. package/dist/system/verify.js.map +1 -0
  120. package/dist/templates.d.ts +14 -1
  121. package/dist/templates.js +134 -5
  122. package/dist/templates.js.map +1 -1
  123. package/dist/util/slug.js +16 -10
  124. package/dist/util/slug.js.map +1 -1
  125. package/package.json +10 -1
@@ -0,0 +1,160 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // `em slice review` (MIL-201): gives `status: reviewed` a mechanical path into the slice doc.
3
+ // `reviewed` has been a first-class value in the status enum since the schema was written
4
+ // (docs/slice-doc-schema.md), but nothing ever wrote it — a facilitator who finished walking a
5
+ // slice in a stakeholder review either hand-edited the frontmatter or (the failure this command
6
+ // exists to prevent) reached straight for `em slice ratify` and collapsed two distinct human
7
+ // gates into one. Review is the FIRST gate: the room agreed the slice reads correctly.
8
+ // Ratification is a separate, later, usually multi-person gate — see
9
+ // docs/process.md#the-slice-lifecycle-gates.
10
+ //
11
+ // Deliberately a clone of ratify.ts's mechanics, not a generalization of them: same note-binding
12
+ // resolution (catalog/docJoin.ts's resolveSliceDocJoin), same `--by`/`--on` validation, same
13
+ // surgical index-splice write via ./frontmatterSurgery.js, same idempotent/refuse-to-overwrite
14
+ // discipline. The two commands read as one family precisely because they are the two ends of the
15
+ // same handoff.
16
+ //
17
+ // Status preconditions (R4, docs/cli.md): legal from `draft` (the ordinary case — a slice doc
18
+ // walked in a review session) and from `reviewed` itself (idempotent re-run, or a refusal when a
19
+ // DIFFERENT reviewer/date is already recorded). Refuses from `ready-to-implement`/`implemented`:
20
+ // review applies BEFORE ratification, and a slice that has already shipped is reopened with
21
+ // `em slice reratify`, not reviewed in place.
22
+ //
23
+ // `reviewedBy`/`reviewedOn` are additive, optional frontmatter keys in every status
24
+ // (docs/slice-doc-schema.md) — exactly like `ratifiedBy`/`ratifiedOn`. `em slice ratify` never
25
+ // clears them (the review record stays as provenance for the version it described);
26
+ // `em slice reratify` does clear them alongside `ratifiedBy`/`ratifiedOn`, since neither the old
27
+ // sign-off nor the old review describes the brand-new version.
28
+ import { readFileSync, writeFileSync } from "node:fs";
29
+ import { join } from "node:path";
30
+ import { resolveSliceDocJoin } from "../catalog/docJoin.js";
31
+ import { fieldLineRegex, locateFrontmatterInner, normalizeFieldValue } from "./frontmatterSurgery.js";
32
+ import { isValidDateString } from "./stateFile.js";
33
+ /** The status this command flips a slice doc to — the review gate
34
+ * (docs/process.md#the-slice-lifecycle-gates): the room walked the slice and every open question
35
+ * it raised was resolved. NOT a handoff to an implementer; that's `em slice ratify`. */
36
+ export const REVIEWED_STATUS = "reviewed";
37
+ /** Statuses `em slice review` refuses to act on: review applies before ratification, so a doc
38
+ * that has already passed the ratification gate (or shipped) is past the point where "the room
39
+ * reviewed it" is a meaningful new fact about THIS version. */
40
+ const POST_REVIEW_STATUSES = ["ready-to-implement", "implemented"];
41
+ /**
42
+ * Pure text transform: flips `status:` to `reviewed` and sets `reviewedBy:`/`reviewedOn:` in
43
+ * `raw`'s frontmatter block. Idempotent (re-applying the same by/on pair once already reviewed is
44
+ * a no-op, `changed: false`, `content` returned byte-identical to `raw`); refuses (`ok: false`)
45
+ * rather than silently overwrite provenance when the doc is already `status: reviewed` with a
46
+ * *different* recorded reviewer/date — mirroring `applyRatifyFrontmatter`'s refusal exactly.
47
+ * Refuses from `ready-to-implement`/`implemented` (see module header). No fs access — the caller
48
+ * reads/writes; see `runReview` below.
49
+ */
50
+ export function applyReviewFrontmatter(raw, reviewedBy, reviewedOn) {
51
+ const trimmedBy = reviewedBy.trim();
52
+ if (!trimmedBy)
53
+ return { ok: false, message: "a reviewer name is required (--by)" };
54
+ // Same guard, same rationale as ratify's: an embedded \r/\n could splice a multi-line value
55
+ // into the frontmatter and corrupt the fence, while internal spaces are legitimate in a name.
56
+ if (/[\x00-\x1f\x7f]/.test(trimmedBy)) {
57
+ return { ok: false, message: "reviewer name must not contain control characters" };
58
+ }
59
+ if (!isValidDateString(reviewedOn)) {
60
+ return { ok: false, message: `invalid date "${reviewedOn}" — expected YYYY-MM-DD` };
61
+ }
62
+ const range = locateFrontmatterInner(raw);
63
+ if (!range)
64
+ return { ok: false, message: "no frontmatter block found" };
65
+ const inner = raw.slice(range.innerStart, range.innerEnd);
66
+ const statusMatch = fieldLineRegex("status").exec(inner);
67
+ if (!statusMatch)
68
+ return { ok: false, message: "no `status:` field found in frontmatter" };
69
+ const byMatch = fieldLineRegex("reviewedBy").exec(inner);
70
+ const onMatch = fieldLineRegex("reviewedOn").exec(inner);
71
+ const currentStatus = normalizeFieldValue(statusMatch[2])?.toLowerCase() ?? null;
72
+ const currentBy = byMatch ? normalizeFieldValue(byMatch[2]) : null;
73
+ const currentOn = onMatch ? normalizeFieldValue(onMatch[2]) : null;
74
+ if (currentStatus === REVIEWED_STATUS && currentBy !== null && currentOn !== null) {
75
+ if (currentBy === trimmedBy && currentOn === reviewedOn) {
76
+ return { ok: true, content: raw, changed: false }; // idempotent no-op
77
+ }
78
+ return {
79
+ ok: false,
80
+ message: `already reviewed by ${currentBy} on ${currentOn} — refusing to overwrite with ` +
81
+ `${trimmedBy} on ${reviewedOn}`,
82
+ };
83
+ }
84
+ if (currentStatus !== null && POST_REVIEW_STATUSES.includes(currentStatus)) {
85
+ return {
86
+ ok: false,
87
+ message: `doc is \`status: ${currentStatus}\` — review applies before ratification; a shipped ` +
88
+ "slice is reopened with `em slice reratify`",
89
+ };
90
+ }
91
+ // Match the file's own line-ending style for any freshly-inserted line — same trick
92
+ // markImplemented.ts/ratify.ts use (see ratify.ts for the full rationale).
93
+ const afterStatus = inner.slice(statusMatch.index + statusMatch[0].length);
94
+ const eol = afterStatus.startsWith("\r\n") ? "\r\n" : "\n";
95
+ // Whichever of reviewedBy/reviewedOn have no existing line get folded directly into the status
96
+ // edit's replacement text (inserted right after the new status value) rather than added as
97
+ // separate zero-length "insert" edits — same convention as ratify.ts.
98
+ const missingLines = [];
99
+ if (!byMatch)
100
+ missingLines.push(`reviewedBy: ${trimmedBy}`);
101
+ if (!onMatch)
102
+ missingLines.push(`reviewedOn: ${reviewedOn}`);
103
+ const newStatusText = `${statusMatch[1]}${REVIEWED_STATUS}`;
104
+ const statusNext = missingLines.length > 0 ? `${newStatusText}${eol}${missingLines.join(eol)}` : newStatusText;
105
+ // Apply from the highest index first so an earlier edit's index stays valid — same convention
106
+ // markImplemented.ts/ratify.ts use.
107
+ const edits = [
108
+ { index: statusMatch.index, oldLen: statusMatch[0].length, next: statusNext },
109
+ ];
110
+ if (byMatch)
111
+ edits.push({ index: byMatch.index, oldLen: byMatch[0].length, next: `${byMatch[1]}${trimmedBy}` });
112
+ if (onMatch)
113
+ edits.push({ index: onMatch.index, oldLen: onMatch[0].length, next: `${onMatch[1]}${reviewedOn}` });
114
+ edits.sort((a, b) => b.index - a.index);
115
+ let updatedInner = inner;
116
+ for (const edit of edits) {
117
+ updatedInner = updatedInner.slice(0, edit.index) + edit.next + updatedInner.slice(edit.index + edit.oldLen);
118
+ }
119
+ const content = raw.slice(0, range.innerStart) + updatedInner + raw.slice(range.innerEnd);
120
+ return { ok: true, content, changed: true };
121
+ }
122
+ /**
123
+ * Resolves `sliceKey` to its bound doc via the same note-binding join `ratify`/`mark-implemented`/
124
+ * `--slice-ready`/`em export` use (MIL-121 cross-binding included), then reads/applies/writes it.
125
+ * `baseDir` is the `.em` file's directory, same convention every doc/note path in `em` uses.
126
+ */
127
+ export function runReview(model, refs, baseDir, sliceKey, reviewedBy, reviewedOn) {
128
+ const sliceIndex = refs.sliceKeys.indexOf(sliceKey);
129
+ if (sliceIndex === -1) {
130
+ return { ok: false, message: `no slice with export key "${sliceKey}" in this model` };
131
+ }
132
+ const slice = model.slices[sliceIndex];
133
+ const { doc } = resolveSliceDocJoin(slice, sliceKey, baseDir, (id) => refs.refById.get(id));
134
+ if (doc.reason === "no-doc-bound") {
135
+ return {
136
+ ok: false,
137
+ message: `slice "${sliceKey}" has no doc bound via \`note "slices/${sliceKey}.md"\` — bind a slice doc before reviewing it`,
138
+ };
139
+ }
140
+ if (doc.reason === "binding-missing-file") {
141
+ return { ok: false, message: `slice "${sliceKey}" notes "${doc.path}" but no such file exists` };
142
+ }
143
+ if (doc.reason === "frontmatter-invalid") {
144
+ return {
145
+ ok: false,
146
+ message: `slice doc "${doc.path}" has missing or invalid frontmatter — run \`em validate\` for details`,
147
+ };
148
+ }
149
+ const absPath = join(baseDir, doc.path);
150
+ const raw = readFileSync(absPath, "utf8");
151
+ const result = applyReviewFrontmatter(raw, reviewedBy, reviewedOn);
152
+ if (!result.ok) {
153
+ return { ok: false, message: `${doc.path}: ${result.message}` };
154
+ }
155
+ if (result.changed) {
156
+ writeFileSync(absPath, result.content, "utf8");
157
+ }
158
+ return { ok: true, path: doc.path, changed: result.changed };
159
+ }
160
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/cli/review.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,9 @@ export interface SliceIndexRow {
9
9
  name: string;
10
10
  pattern: string;
11
11
  status: string;
12
+ /** MIL-201: the doc's `reviewedBy`, or `—`. Sits between Status and Ratified by so the two
13
+ * human gates read left-to-right in the order they happen. */
14
+ reviewedBy: string;
12
15
  ratifiedBy: string;
13
16
  owner: string;
14
17
  tracking: string;
@@ -16,8 +16,8 @@ import { applyMarker } from "../util/markers.js";
16
16
  /** The marker name wrapping the Slices table: `<!-- GENERATED:slices:start -->` /
17
17
  * `<!-- GENERATED:slices:end -->`, matching templates/model-readme.md. */
18
18
  export const SLICE_INDEX_MARKER = "slices";
19
- const TABLE_HEADER = "| # | Slice | Pattern | Status | Ratified by | Owner | Tracking | Implemented in | Design doc |\n" +
20
- "|---|-------|---------|--------|-------------|-------|----------|----------------|------------|";
19
+ const TABLE_HEADER = "| # | Slice | Pattern | Status | Reviewed by | Ratified by | Owner | Tracking | Implemented in | Design doc |\n" +
20
+ "|---|-------|---------|--------|-------------|-------------|-------|----------|----------------|------------|";
21
21
  /** Escape characters that would break a markdown table cell: `|` (the column separator) and
22
22
  * newlines (a slice name is always one line, but a doc's freeform `implementedIn` text isn't
23
23
  * guaranteed to be). */
@@ -53,6 +53,7 @@ export function buildSliceIndexTable(model, refs, baseDir) {
53
53
  name: slice.name,
54
54
  pattern: slicePatternLabel(pattern),
55
55
  status: statusCell(doc),
56
+ reviewedBy: doc.reviewedBy ?? "—",
56
57
  ratifiedBy: doc.ratifiedBy ?? "—",
57
58
  owner: doc.owner ?? "—",
58
59
  tracking: doc.tracking ?? "—",
@@ -61,8 +62,8 @@ export function buildSliceIndexTable(model, refs, baseDir) {
61
62
  };
62
63
  });
63
64
  const lines = rows.map((r) => `| ${r.index} | ${escapeCell(r.name)} | ${escapeCell(r.pattern)} | ${escapeCell(r.status)} | ` +
64
- `${escapeCell(r.ratifiedBy)} | ${escapeCell(r.owner)} | ${escapeCell(r.tracking)} | ` +
65
- `${escapeCell(r.implementedIn)} | [${r.docPath}](${r.docPath}) |`);
65
+ `${escapeCell(r.reviewedBy)} | ${escapeCell(r.ratifiedBy)} | ${escapeCell(r.owner)} | ` +
66
+ `${escapeCell(r.tracking)} | ${escapeCell(r.implementedIn)} | [${r.docPath}](${r.docPath}) |`);
66
67
  return { markdown: [TABLE_HEADER, ...lines].join("\n"), rows, diagnostics };
67
68
  }
68
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"sliceIndex.js","sourceRoot":"","sources":["../../src/cli/sliceIndex.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,6FAA6F;AAC7F,6FAA6F;AAC7F,sFAAsF;AACtF,iGAAiG;AACjG,EAAE;AACF,0FAA0F;AAC1F,uFAAuF;AACvF,+FAA+F;AAC/F,sDAAsD;AAEtD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAI1C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAkB,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;2EAC2E;AAC3E,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE3C,MAAM,YAAY,GAChB,mGAAmG;IACnG,iGAAiG,CAAC;AAEpG;;yBAEyB;AACzB,SAAS,UAAU,CAAC,CAAS;IAC3B,wFAAwF;IACxF,yFAAyF;IACzF,wFAAwF;IACxF,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;8EAM8E;AAC9E,SAAS,UAAU,CAAC,GAAmB;IACrC,IAAI,CAAC,GAAG,CAAC,KAAK;QAAE,OAAO,YAAY,CAAC;IACpC,OAAO,GAAG,CAAC,MAAM,IAAI,SAAS,CAAC;AACjC,CAAC;AAwBD;oFACoF;AACpF,MAAM,UAAU,oBAAoB,CAAC,KAAsB,EAAE,IAAgB,EAAE,OAAe;IAC5F,MAAM,WAAW,GAAiB,EAAE,CAAC;IAErC,MAAM,IAAI,GAAoB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,mBAAmB,CACxD,KAAK,EACL,QAAQ,EACR,OAAO,EACP,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAE,CAC9B,CAAC;QACF,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC9B,OAAO;YACL,KAAK,EAAE,CAAC,GAAG,CAAC;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACnC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC;YACvB,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG;YACjC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG;YACvB,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,GAAG;YAC7B,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,GAAG;YACvC,OAAO,EAAE,GAAG,CAAC,IAAI;SAClB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,CAAC,EAAE,EAAE,CACJ,KAAK,CAAC,CAAC,KAAK,MAAM,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK;QAC9F,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK;QACrF,GAAG,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CACpE,CAAC;IAEF,OAAO,EAAE,QAAQ,EAAE,CAAC,YAAY,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;AAC9E,CAAC;AAMD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAsB,EACtB,IAAgB,EAChB,UAAkB,EAClB,KAAc;IAEd,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAEzD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,gBAAgB;YACtB,OAAO,EACL,gDAAgD,UAAU,gBAAgB,UAAU,MAAM;gBAC1F,gEAAgE;YAClE,KAAK;SACN,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1E,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,iBAAiB;YACvB,OAAO,EACL,mBAAmB,UAAU,0BAA0B,kBAAkB,eAAe;gBACxF,kBAAkB,kBAAkB,sDAAsD;gBAC1F,4FAA4F;gBAC5F,8BAA8B;YAChC,KAAK;SACN,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,KAAK,QAAQ,CAAC;IACrC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAEnF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,OAAO;YACb,OAAO,EACL,2BAA2B,UAAU,iCAAiC;gBACtE,oBAAoB,UAAU,2BAA2B;YAC3D,KAAK;SACN,CAAC;IACJ,CAAC;IAED,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"sliceIndex.js","sourceRoot":"","sources":["../../src/cli/sliceIndex.ts"],"names":[],"mappings":"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"}
@@ -55,6 +55,16 @@ export declare function loadStateFile(dirOrFile: string): LoadResult;
55
55
  * review:` holds neither `never` nor its documented format — both are the file failing to be
56
56
  * the thing resume/conform scoping needs, so both are reported the same way. */
57
57
  export declare function parseState(text: string): ParseStateResult;
58
+ /** A state file is shared by every `.em` file in its directory (`.event-modeling.md` has no
59
+ * per-model namespacing), but its `Model file:` bullet names exactly ONE of them — so a
60
+ * sibling file it does NOT describe (the common case: a `conform-scope --seed-asis` scratch
61
+ * copy like `checkout-asis.em` sitting next to `checkout.em`) must not inherit `checkout.em`'s
62
+ * conformance record just because it lives in the same directory. Returns the exact non-fatal
63
+ * message to report when `modelPath` (a parsed state's `modelPath`, already stripped of
64
+ * backticks) names a different file than `file`'s basename; `null` when they agree. THE one
65
+ * check both `em status` (`resolveConformanceEntry`, PR #116/MIL-163) and `em conform-scope`
66
+ * (MIL-179) run before attributing a state file's `Last conformance:` to a given model file. */
67
+ export declare function modelPathMismatch(modelPath: string, file: string): string | null;
58
68
  /** `em state set-phase`: rewrite `Current phase:` (and `Current step:` when `step` is given)
59
69
  * plus `Last updated:`. `phase` is trusted to already be a validated `Phase` — the CLI layer
60
70
  * checks against `PHASES` before calling this so the enum has exactly one home. */
@@ -122,6 +122,20 @@ export function parseState(text) {
122
122
  },
123
123
  };
124
124
  }
125
+ /** A state file is shared by every `.em` file in its directory (`.event-modeling.md` has no
126
+ * per-model namespacing), but its `Model file:` bullet names exactly ONE of them — so a
127
+ * sibling file it does NOT describe (the common case: a `conform-scope --seed-asis` scratch
128
+ * copy like `checkout-asis.em` sitting next to `checkout.em`) must not inherit `checkout.em`'s
129
+ * conformance record just because it lives in the same directory. Returns the exact non-fatal
130
+ * message to report when `modelPath` (a parsed state's `modelPath`, already stripped of
131
+ * backticks) names a different file than `file`'s basename; `null` when they agree. THE one
132
+ * check both `em status` (`resolveConformanceEntry`, PR #116/MIL-163) and `em conform-scope`
133
+ * (MIL-179) run before attributing a state file's `Last conformance:` to a given model file. */
134
+ export function modelPathMismatch(modelPath, file) {
135
+ if (!modelPath || modelPath === basename(file))
136
+ return null;
137
+ return `state file describes "${modelPath}", not "${basename(file)}" — not attributing its conformance record`;
138
+ }
125
139
  /** Replace one or more bullet lines in place, in the order given, failing (without writing
126
140
  * anything) if any targeted bullet is missing. Every other byte of `text` — including every
127
141
  * other bullet, and whatever line ending style the file already used — passes through the
@@ -1 +1 @@
1
- {"version":3,"file":"stateFile.js","sourceRoot":"","sources":["../../src/cli/stateFile.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,2EAA2E;AAC3E,+FAA+F;AAC/F,6FAA6F;AAC7F,8FAA8F;AAC9F,6FAA6F;AAC7F,gGAAgG;AAChG,+CAA+C;AAC/C,EAAE;AACF,4FAA4F;AAC5F,6FAA6F;AAC7F,+FAA+F;AAC/F,cAAc;AACd,EAAE;AACF,iGAAiG;AACjG,4FAA4F;AAC5F,qEAAqE;AAErE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE3C,8EAA8E;AAC9E,MAAM,CAAC,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAEpD;;;2FAG2F;AAC3F,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAU,CAAC;AAGvH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAQ,MAA4B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,MAAM,GAAG;IACb,SAAS,EAAE,YAAY;IACvB,YAAY,EAAE,eAAe;IAC7B,WAAW,EAAE,cAAc;IAC3B,WAAW,EAAE,cAAc;IAC3B,eAAe,EAAE,kBAAkB;IACnC,qBAAqB,EAAE,yBAAyB;CACxC,CAAC;AAiBX,SAAS,cAAc,CAAC,CAAS;IAC/B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,4FAA4F;IAC5F,oDAAoD;IACpD,OAAO,IAAI,MAAM,CAAC,eAAe,cAAc,CAAC,KAAK,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,KAAa;IAC9C,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,eAAe,cAAc,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACnF,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChC,CAAC;AAED;;;oFAGoF;AACpF,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;AAChG,CAAC;AAID,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,IAAI,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,IAAI,EAAE,EAAE,CAAC;IACtE,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,mBAAmB,GAAG,8CAA8C,CAAC;AAC3E,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD;;;iFAGiF;AACjF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;YAC/B,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,2BAA2B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SACrF,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAE9D,MAAM,kBAAkB,GAAG,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACvD,IAAI,eAAe,GAAmC,IAAI,CAAC;IAC3D,IAAI,kBAAkB,KAAK,OAAO,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,mBAAmB,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACvD,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,OAAO,EAAE,kGAAkG,kBAAkB,EAAE;aAChI,CAAC;QACJ,CAAC;QACD,eAAe,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,CAAC;IAED,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACxD,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,eAAe,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9C,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,OAAO,EAAE,gFAAgF,aAAa,EAAE;aACzG,CAAC;QACJ,CAAC;QACD,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE;YACL,SAAS;YACT,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC;YAC/B,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;YAC7B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;YACpC,eAAe;YACf,UAAU;SACX;KACF,CAAC;AACJ,CAAC;AAED;;;uBAGuB;AACvB,SAAS,kBAAkB,CAAC,IAAY,EAAE,OAAgD;IACxF,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;QACvC,MAAM,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,SAAS;QACX,CAAC;QACD,0FAA0F;QAC1F,yFAAyF;QACzF,8EAA8E;QAC9E,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,KAAK,OAAO,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,2BAA2B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;IAC7G,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED;;oFAEoF;AACpF,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAY,EAAE,KAAa,EAAE,IAAa;IAC/E,MAAM,OAAO,GAA4C,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACxG,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjF,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,OAAO,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED;;;iBAGiB;AACjB,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,QAAgB,EAAE,MAAc,EAAE,KAAa;IAC1F,MAAM,KAAK,GAAG,GAAG,KAAK,MAAM,QAAQ,cAAc,MAAM,EAAE,CAAC;IAC3D,OAAO,kBAAkB,CAAC,IAAI,EAAE;QAC9B,EAAE,KAAK,EAAE,MAAM,CAAC,eAAe,EAAE,KAAK,EAAE;QACxC,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE;KAC5C,CAAC,CAAC;AACL,CAAC;AAED;;qCAEqC;AACrC,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY,EAAE,KAAa;IACjE,MAAM,KAAK,GAAG,GAAG,IAAI,gCAAgC,CAAC;IACtD,OAAO,kBAAkB,CAAC,IAAI,EAAE;QAC9B,EAAE,KAAK,EAAE,MAAM,CAAC,qBAAqB,EAAE,KAAK,EAAE;QAC9C,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE;KAC5C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,OAAO,GAAG,2BAA2B,CAAC;AAE5C;;+BAE+B;AAC/B,MAAM,UAAU,iBAAiB,CAAC,CAAS;IACzC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACrB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"stateFile.js","sourceRoot":"","sources":["../../src/cli/stateFile.ts"],"names":[],"mappings":"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"}
@@ -91,6 +91,11 @@ export interface ConformanceEntry {
91
91
  * `resolveConformanceEntry` below): a bare "opted out of computing this" state doesn't occur
92
92
  * on the real CLI/MCP call paths, which always supply `sliceDocFacts`. */
93
93
  slicePRsBehindHead: number | null;
94
+ /** MIL-202: whether this model's implementation constitution exists, and where it's expected
95
+ * — existence only. `em` never reads or validates the document's content (no LLM, nothing
96
+ * parsed): the whole fact is "is the house-rules document there or not". See
97
+ * `resolveConstitution` for the two-shape resolution rule. */
98
+ constitution: ConstitutionEntry;
94
99
  /** Set when the state file exists and parses, but git couldn't answer commits-behind-HEAD or
95
100
  * slice-PRs-behind-HEAD (not a git repo, unknown revision, ...) — reported per-model rather
96
101
  * than aborting the whole rollup, since `em status` is a soft report, not a gate. Also carries
@@ -98,6 +103,37 @@ export interface ConformanceEntry {
98
103
  * treatment. */
99
104
  error: string | null;
100
105
  }
106
+ /** Where the project's implementation constitution is, and whether it's there. `path` is always
107
+ * the EXPECTED location — set whether or not the file exists — expressed **relative to the
108
+ * entry's own `modelDir`**, always with `/` separators, so the JSON document stays
109
+ * machine-independent (never an absolute path, never a Windows separator). `join(modelDir,
110
+ * path)` reconstructs the real location. Typed `string | null` for the ruling's shape; on every
111
+ * real call path (`resolveConstitution` below) it is non-null, since the expected location is
112
+ * always computable from `modelDir` alone. */
113
+ export interface ConstitutionEntry {
114
+ present: boolean;
115
+ path: string | null;
116
+ }
117
+ /** Walk up from `startDir` looking for a `.specify/` directory — the same "is this a spec-kit
118
+ * project, and where is its root" walk `em-sdd-bridge`'s `findRepoRoot` does (and spec-kit's own
119
+ * `find_specify_root`). Returns the directory CONTAINING `.specify/`, or null at the filesystem
120
+ * root. Directory existence only; nothing inside `.specify/` is read. */
121
+ export declare function findSpecifyRoot(startDir: string): string | null;
122
+ /**
123
+ * Resolve one model's implementation constitution (MIL-202). Two project shapes, one document —
124
+ * em never writes or expects a second copy:
125
+ *
126
+ * - **spec-kit project** (a `.specify/` directory exists at or above `modelDir`): the document
127
+ * IS `<root>/.specify/memory/constitution.md`, spec-kit's own slot. em defers to it.
128
+ * - **anything else**: `<modelDir>/constitution.md`, beside the model — what `em scaffold`
129
+ * writes.
130
+ *
131
+ * Existence only: this never opens the file, so a blank stock template and a fully answered,
132
+ * ratified constitution are indistinguishable here by design (content judgment is a human's, and
133
+ * `em`'s core stays deterministic and LLM-free). The advisory "is it still an unfilled template"
134
+ * check belongs to the SDD bridge, not here.
135
+ */
136
+ export declare function resolveConstitution(modelDir: string): ConstitutionEntry;
101
137
  /**
102
138
  * `git rev-list --count <revision>..HEAD` in `repo` (any directory inside it, not necessarily
103
139
  * its root — same "anchor dir, resolve toplevel" convention `conformScope.ts`'s
@@ -142,9 +178,10 @@ export declare function resolveSlicePRsBehindHead(repo: string, revision: string
142
178
  * per-model namespacing), but its `Model file:` bullet names exactly ONE of them — so a sibling
143
179
  * file it does NOT describe (the common case: a `conform-scope --seed-asis` scratch copy like
144
180
  * `checkout-asis.em` sitting next to `checkout.em`) must not inherit `checkout.em`'s conformance
145
- * record just because it lives in the same directory. When `Model file:` is set and doesn't match
146
- * `basename(file)`, the record is reported as unattributed (via `error`, the same non-fatal
147
- * channel a git failure uses) rather than silently misattributed.
181
+ * record just because it lives in the same directory. `stateFile.ts`'s `modelPathMismatch` (the
182
+ * one check both this function and `em conform-scope`, MIL-179, run) reports that case: the
183
+ * record comes back unattributed (via `error`, the same non-fatal channel a git failure uses)
184
+ * rather than silently misattributed.
148
185
  */
149
186
  export declare function resolveConformanceEntry(file: string, repoOverride: string | undefined, sliceDocFacts: SliceDocFacts[], runGit?: GitRunner): ConformanceEntry;
150
187
  export interface StatusSliceCounts {
@@ -238,6 +275,10 @@ export declare function aggregateInvariantTotals(testsDir: string, reports: Cove
238
275
  /** Exported for `em freshness` (freshness.ts, MIL-164) — the standalone surface for exactly
239
276
  * this one clause, without pulling in the rest of `em status`'s rollup. */
240
277
  export declare function formatConformancePart(entry: ConformanceEntry): string;
278
+ /** MIL-202: the per-model constitution clause — `present`, or `absent (<expected path>)` with
279
+ * the path the entry already resolved (relative to that model's own directory). Shared by the
280
+ * text and markdown reports so the two can't drift. Existence only; never a content judgment. */
281
+ export declare function formatConstitutionPart(entry: ConformanceEntry): string;
241
282
  /** The one-line rollup: `"8/8 implemented · 20/20 invariants covered · 0 open issues · last
242
283
  * conformed <rev>, N commits behind HEAD"` (MIL-163's acceptance line). For multiple input
243
284
  * models, the conformance clause reports the FIRST file's entry — a single-file invocation is
@@ -21,11 +21,12 @@
21
21
  // Deterministic core: no LLM calls, no wall-clock timestamps in the output, byte-stable for the
22
22
  // same inputs. Text/markdown/badge are formatting layers over one aggregated StatusReport; the
23
23
  // JSON document (emit/statusJson.ts) is a versioned envelope around the exact same object.
24
- import { basename, dirname, resolve } from "node:path";
24
+ import { existsSync } from "node:fs";
25
+ import { dirname, join, relative, resolve, sep } from "node:path";
25
26
  import { resolveSliceDocJoin } from "../catalog/docJoin.js";
26
27
  import { readSliceDoc } from "../catalog/readSliceDoc.js";
27
28
  import { realGit } from "./diff-inputs.js";
28
- import { loadStateFile, parseState } from "./stateFile.js";
29
+ import { loadStateFile, parseState, modelPathMismatch } from "./stateFile.js";
29
30
  import { changedPathsSince, buildConformScope } from "./conformScope.js";
30
31
  /** The 4 canonical slice-doc lifecycle statuses (docs/slice-doc-schema.md) — the same enum
31
32
  * `em catalog`'s header coloring and `em render`'s status legend already recognize. */
@@ -87,6 +88,41 @@ export function resolveSliceStatusFacts(file, model, refs, baseDir) {
87
88
  export function countOpenIssues(model) {
88
89
  return model.elements.filter((el) => el.issue).length;
89
90
  }
91
+ /** Walk up from `startDir` looking for a `.specify/` directory — the same "is this a spec-kit
92
+ * project, and where is its root" walk `em-sdd-bridge`'s `findRepoRoot` does (and spec-kit's own
93
+ * `find_specify_root`). Returns the directory CONTAINING `.specify/`, or null at the filesystem
94
+ * root. Directory existence only; nothing inside `.specify/` is read. */
95
+ export function findSpecifyRoot(startDir) {
96
+ let dir = resolve(startDir);
97
+ for (;;) {
98
+ if (existsSync(join(dir, ".specify")))
99
+ return dir;
100
+ const parent = dirname(dir);
101
+ if (parent === dir)
102
+ return null;
103
+ dir = parent;
104
+ }
105
+ }
106
+ /**
107
+ * Resolve one model's implementation constitution (MIL-202). Two project shapes, one document —
108
+ * em never writes or expects a second copy:
109
+ *
110
+ * - **spec-kit project** (a `.specify/` directory exists at or above `modelDir`): the document
111
+ * IS `<root>/.specify/memory/constitution.md`, spec-kit's own slot. em defers to it.
112
+ * - **anything else**: `<modelDir>/constitution.md`, beside the model — what `em scaffold`
113
+ * writes.
114
+ *
115
+ * Existence only: this never opens the file, so a blank stock template and a fully answered,
116
+ * ratified constitution are indistinguishable here by design (content judgment is a human's, and
117
+ * `em`'s core stays deterministic and LLM-free). The advisory "is it still an unfilled template"
118
+ * check belongs to the SDD bridge, not here.
119
+ */
120
+ export function resolveConstitution(modelDir) {
121
+ const base = resolve(modelDir);
122
+ const specifyRoot = findSpecifyRoot(base);
123
+ const absolute = specifyRoot ? join(specifyRoot, ".specify", "memory", "constitution.md") : join(base, "constitution.md");
124
+ return { present: existsSync(absolute), path: relative(base, absolute).split(sep).join("/") };
125
+ }
90
126
  /**
91
127
  * `git rev-list --count <revision>..HEAD` in `repo` (any directory inside it, not necessarily
92
128
  * its root — same "anchor dir, resolve toplevel" convention `conformScope.ts`'s
@@ -146,16 +182,20 @@ export function resolveSlicePRsBehindHead(repo, revision, slices, runGit = realG
146
182
  * per-model namespacing), but its `Model file:` bullet names exactly ONE of them — so a sibling
147
183
  * file it does NOT describe (the common case: a `conform-scope --seed-asis` scratch copy like
148
184
  * `checkout-asis.em` sitting next to `checkout.em`) must not inherit `checkout.em`'s conformance
149
- * record just because it lives in the same directory. When `Model file:` is set and doesn't match
150
- * `basename(file)`, the record is reported as unattributed (via `error`, the same non-fatal
151
- * channel a git failure uses) rather than silently misattributed.
185
+ * record just because it lives in the same directory. `stateFile.ts`'s `modelPathMismatch` (the
186
+ * one check both this function and `em conform-scope`, MIL-179, run) reports that case: the
187
+ * record comes back unattributed (via `error`, the same non-fatal channel a git failure uses)
188
+ * rather than silently misattributed.
152
189
  */
153
190
  export function resolveConformanceEntry(file, repoOverride, sliceDocFacts, runGit = realGit) {
154
191
  const modelDir = dirname(file);
155
192
  const repo = repoOverride ?? modelDir;
193
+ // MIL-202: independent of the state file and of git — the constitution is a per-model fact
194
+ // every entry carries, including the early-return branches below.
195
+ const constitution = resolveConstitution(modelDir);
156
196
  const loaded = loadStateFile(modelDir);
157
197
  if (!loaded.ok) {
158
- return { file, modelDir, hasStateFile: false, lastConformance: null, repo, commitsBehindHead: null, slicePRsBehindHead: null, error: null };
198
+ return { file, modelDir, hasStateFile: false, lastConformance: null, repo, commitsBehindHead: null, slicePRsBehindHead: null, constitution, error: null };
159
199
  }
160
200
  const parsed = parseState(loaded.text);
161
201
  if (!parsed.ok) {
@@ -167,23 +207,16 @@ export function resolveConformanceEntry(file, repoOverride, sliceDocFacts, runGi
167
207
  repo,
168
208
  commitsBehindHead: null,
169
209
  slicePRsBehindHead: null,
210
+ constitution,
170
211
  error: `state file: ${parsed.message}`,
171
212
  };
172
213
  }
173
- if (parsed.state.modelPath && parsed.state.modelPath !== basename(file)) {
174
- return {
175
- file,
176
- modelDir,
177
- hasStateFile: true,
178
- lastConformance: null,
179
- repo,
180
- commitsBehindHead: null,
181
- slicePRsBehindHead: null,
182
- error: `state file describes "${parsed.state.modelPath}", not "${basename(file)}" — not attributing its conformance record`,
183
- };
214
+ const mismatch = modelPathMismatch(parsed.state.modelPath, file);
215
+ if (mismatch) {
216
+ return { file, modelDir, hasStateFile: true, lastConformance: null, repo, commitsBehindHead: null, slicePRsBehindHead: null, constitution, error: mismatch };
184
217
  }
185
218
  if (!parsed.state.lastConformance) {
186
- return { file, modelDir, hasStateFile: true, lastConformance: null, repo, commitsBehindHead: null, slicePRsBehindHead: null, error: null };
219
+ return { file, modelDir, hasStateFile: true, lastConformance: null, repo, commitsBehindHead: null, slicePRsBehindHead: null, constitution, error: null };
187
220
  }
188
221
  const { date, revision } = parsed.state.lastConformance;
189
222
  const commitsResult = commitsBehindHead(repo, revision, runGit);
@@ -196,6 +229,7 @@ export function resolveConformanceEntry(file, repoOverride, sliceDocFacts, runGi
196
229
  repo,
197
230
  commitsBehindHead: null,
198
231
  slicePRsBehindHead: null,
232
+ constitution,
199
233
  error: commitsResult.message,
200
234
  };
201
235
  }
@@ -209,6 +243,7 @@ export function resolveConformanceEntry(file, repoOverride, sliceDocFacts, runGi
209
243
  repo,
210
244
  commitsBehindHead: commitsResult.count,
211
245
  slicePRsBehindHead: null,
246
+ constitution,
212
247
  error: slicePRsResult.message,
213
248
  };
214
249
  }
@@ -220,6 +255,7 @@ export function resolveConformanceEntry(file, repoOverride, sliceDocFacts, runGi
220
255
  repo,
221
256
  commitsBehindHead: commitsResult.count,
222
257
  slicePRsBehindHead: slicePRsResult.count,
258
+ constitution,
223
259
  error: null,
224
260
  };
225
261
  }
@@ -361,6 +397,12 @@ function formatConformanceValue(entry) {
361
397
  return "never conformed";
362
398
  return `\`${entry.lastConformance.revision}\` — ${formatBehindHead(entry)}`;
363
399
  }
400
+ /** MIL-202: the per-model constitution clause — `present`, or `absent (<expected path>)` with
401
+ * the path the entry already resolved (relative to that model's own directory). Shared by the
402
+ * text and markdown reports so the two can't drift. Existence only; never a content judgment. */
403
+ export function formatConstitutionPart(entry) {
404
+ return entry.constitution.present ? "present" : `absent (${entry.constitution.path ?? "unresolved"})`;
405
+ }
364
406
  /** The one-line rollup: `"8/8 implemented · 20/20 invariants covered · 0 open issues · last
365
407
  * conformed <rev>, N commits behind HEAD"` (MIL-163's acceptance line). For multiple input
366
408
  * models, the conformance clause reports the FIRST file's entry — a single-file invocation is
@@ -398,6 +440,12 @@ export function formatStatusDetail(report) {
398
440
  const label = multi ? `conformance (${entry.file}): ` : "conformance: ";
399
441
  lines.push(`${label}${formatConformancePart(entry)}`);
400
442
  }
443
+ // MIL-202: one constitution line per model, after the conformance lines, same multi-model
444
+ // labelling convention.
445
+ for (const entry of report.conformance) {
446
+ const label = multi ? `constitution (${entry.file}): ` : "constitution: ";
447
+ lines.push(`${label}${formatConstitutionPart(entry)}`);
448
+ }
401
449
  if (report.diagnostics.length > 0) {
402
450
  lines.push(`doc issues: ${pluralize(report.diagnostics.length, "warning")} — see diagnostics (${report.diagnostics.map((d) => d.code).join(", ")})`);
403
451
  }
@@ -440,6 +488,15 @@ export function formatStatusMarkdown(report) {
440
488
  rows.push([`Last conformed (${entry.file})`, formatConformanceValue(entry)]);
441
489
  }
442
490
  }
491
+ // MIL-202: one Constitution row per model, same single-vs-multi labelling as "Last conformed".
492
+ if (report.conformance.length <= 1) {
493
+ rows.push(["Constitution", formatConstitutionPart(report.conformance[0])]);
494
+ }
495
+ else {
496
+ for (const entry of report.conformance) {
497
+ rows.push([`Constitution (${entry.file})`, formatConstitutionPart(entry)]);
498
+ }
499
+ }
443
500
  const header = "| Metric | Value |\n|---|---|";
444
501
  const body = rows.map(([k, v]) => `| ${escapeCell(k)} | ${escapeCell(v)} |`).join("\n");
445
502
  return `${header}\n${body}`;