@milehimikey/em 1.12.0 → 1.13.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 (105) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +4 -2
  2. package/.claude/skills/event-modeling-conform/SKILL.md +20 -9
  3. package/.claude/skills/event-modeling-conform/reference/conform.md +107 -10
  4. package/.claude/skills/event-modeling-design/SKILL.md +10 -2
  5. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  6. package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
  7. package/.claude/skills/event-modeling-implement/reference/implement.md +12 -6
  8. package/.claude/skills/event-modeling-review/SKILL.md +5 -1
  9. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +40 -2
  10. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +13 -3
  11. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +30 -0
  12. package/.claude/skills/event-modeling-shared/templates/conformance-report.md +8 -1
  13. package/.claude/skills/event-modeling-shared/templates/state.md +3 -0
  14. package/README.md +1 -0
  15. package/dist/catalog/docJoin.d.ts +9 -0
  16. package/dist/catalog/docJoin.js +6 -0
  17. package/dist/catalog/docJoin.js.map +1 -1
  18. package/dist/catalog/driftSignal.d.ts +11 -5
  19. package/dist/catalog/driftSignal.js +18 -4
  20. package/dist/catalog/driftSignal.js.map +1 -1
  21. package/dist/catalog/modelVersionValidate.d.ts +9 -0
  22. package/dist/catalog/modelVersionValidate.js +38 -0
  23. package/dist/catalog/modelVersionValidate.js.map +1 -0
  24. package/dist/catalog/sliceDoc.d.ts +12 -0
  25. package/dist/catalog/sliceDoc.js +15 -0
  26. package/dist/catalog/sliceDoc.js.map +1 -1
  27. package/dist/cli/ciInit.js +17 -0
  28. package/dist/cli/ciInit.js.map +1 -1
  29. package/dist/cli/conformScope.d.ts +1 -0
  30. package/dist/cli/conformScope.js +3 -1
  31. package/dist/cli/conformScope.js.map +1 -1
  32. package/dist/cli/conformSupersede.d.ts +24 -2
  33. package/dist/cli/conformSupersede.js +44 -3
  34. package/dist/cli/conformSupersede.js.map +1 -1
  35. package/dist/cli/findings.d.ts +134 -0
  36. package/dist/cli/findings.js +355 -0
  37. package/dist/cli/findings.js.map +1 -0
  38. package/dist/cli/metrics.d.ts +93 -0
  39. package/dist/cli/metrics.js +331 -0
  40. package/dist/cli/metrics.js.map +1 -0
  41. package/dist/cli/modelVersion.d.ts +139 -0
  42. package/dist/cli/modelVersion.js +256 -0
  43. package/dist/cli/modelVersion.js.map +1 -0
  44. package/dist/cli/reratify.d.ts +17 -0
  45. package/dist/cli/reratify.js +25 -1
  46. package/dist/cli/reratify.js.map +1 -1
  47. package/dist/cli/skillCheck.js +2 -2
  48. package/dist/cli/skillCheck.js.map +1 -1
  49. package/dist/cli/sliceConform.d.ts +50 -0
  50. package/dist/cli/sliceConform.js +206 -0
  51. package/dist/cli/sliceConform.js.map +1 -0
  52. package/dist/cli/sliceIndex.d.ts +2 -0
  53. package/dist/cli/sliceIndex.js +15 -3
  54. package/dist/cli/sliceIndex.js.map +1 -1
  55. package/dist/cli/sliceNew.d.ts +12 -0
  56. package/dist/cli/sliceNew.js +23 -0
  57. package/dist/cli/sliceNew.js.map +1 -1
  58. package/dist/cli/sliceStubAll.d.ts +76 -0
  59. package/dist/cli/sliceStubAll.js +189 -0
  60. package/dist/cli/sliceStubAll.js.map +1 -0
  61. package/dist/cli/stateFile.d.ts +63 -7
  62. package/dist/cli/stateFile.js +179 -18
  63. package/dist/cli/stateFile.js.map +1 -1
  64. package/dist/cli/status.d.ts +79 -1
  65. package/dist/cli/status.js +134 -15
  66. package/dist/cli/status.js.map +1 -1
  67. package/dist/cli/upgrade.d.ts +122 -0
  68. package/dist/cli/upgrade.js +476 -0
  69. package/dist/cli/upgrade.js.map +1 -0
  70. package/dist/cli.js +524 -36
  71. package/dist/cli.js.map +1 -1
  72. package/dist/emit/freshnessJson.d.ts +1 -1
  73. package/dist/emit/freshnessJson.js +3 -1
  74. package/dist/emit/freshnessJson.js.map +1 -1
  75. package/dist/emit/json.d.ts +11 -1
  76. package/dist/emit/json.js +35 -1
  77. package/dist/emit/json.js.map +1 -1
  78. package/dist/emit/metricsJson.d.ts +7 -0
  79. package/dist/emit/metricsJson.js +27 -0
  80. package/dist/emit/metricsJson.js.map +1 -0
  81. package/dist/emit/statusJson.d.ts +1 -1
  82. package/dist/emit/statusJson.js +17 -1
  83. package/dist/emit/statusJson.js.map +1 -1
  84. package/dist/emit/upgradeJson.d.ts +5 -0
  85. package/dist/emit/upgradeJson.js +26 -0
  86. package/dist/emit/upgradeJson.js.map +1 -0
  87. package/dist/emit/validateJson.d.ts +6 -2
  88. package/dist/emit/validateJson.js +12 -5
  89. package/dist/emit/validateJson.js.map +1 -1
  90. package/dist/mcp/server.d.ts +1 -1
  91. package/dist/mcp/server.js +114 -3
  92. package/dist/mcp/server.js.map +1 -1
  93. package/dist/model/rules.d.ts +7 -0
  94. package/dist/model/rules.js +12 -0
  95. package/dist/model/rules.js.map +1 -1
  96. package/dist/model/validate.d.ts +20 -0
  97. package/dist/model/validate.js +94 -45
  98. package/dist/model/validate.js.map +1 -1
  99. package/dist/templates.d.ts +2 -2
  100. package/dist/templates.js +7 -1
  101. package/dist/templates.js.map +1 -1
  102. package/dist/util/semver.d.ts +19 -0
  103. package/dist/util/semver.js +54 -0
  104. package/dist/util/semver.js.map +1 -0
  105. package/package.json +1 -1
@@ -0,0 +1,331 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // `em metrics --from <rev>` (MIL-170): the four pilot metrics named in advance by the register —
3
+ // conform-cycle cadence + finding counts, ratification turnaround, status-vs-reality
4
+ // disagreement, and whether the readiness gate changes what gets built — computed from git
5
+ // history alone. Three of the four are deterministically computable; the fourth
6
+ // (`readinessGateEffect`) is not and is reported as such rather than approximated by a proxy.
7
+ //
8
+ // Same seams every other historical/git-facing module in this file reuses rather than
9
+ // re-deriving: the injectable `GitRunner`/`realGit` convention (diff-inputs.ts), `resolveRevision`/
10
+ // `resolveDocAtRevision`/`listSliceKeysAtRevision` for reading a slice doc or key list at a
11
+ // revision, `classifyImplementationDrift` (catalog/driftSignal.ts) for the disagreement signal,
12
+ // `parseState`/`STATE_FILE_NAME` (stateFile.ts) for the `Last conformance:` marker, and
13
+ // `findingsPathForReport`/`validateFindingsShape` (findings.ts) for the findings record. No
14
+ // second git wrapper, no second doc parser.
15
+ //
16
+ // Deterministic by construction: every date comes from git's own `--date=format:%Y-%m-%d`
17
+ // (which renders using the TIMEZONE RECORDED WITH THE COMMIT, not the machine running `em` —
18
+ // so it's identical on any machine, unlike `--date=format-local`), every list is either
19
+ // naturally ordered by `--reverse` (oldest first) or explicitly sorted before being returned,
20
+ // and nothing reads "now" — a range `--from <rev> --to <rev>` (or the working `HEAD` default)
21
+ // produces byte-identical output for the same repository state, run twice.
22
+ import { realpathSync } from "node:fs";
23
+ import { dirname, join, relative, resolve } from "node:path";
24
+ import { realGit, resolveRevision, resolveDocAtRevision, listSliceKeysAtRevision } from "./diff-inputs.js";
25
+ import { classifyImplementationDrift } from "../catalog/driftSignal.js";
26
+ import { parseState, STATE_FILE_NAME } from "./stateFile.js";
27
+ import { findingsPathForReport, validateFindingsShape } from "./findings.js";
28
+ // git's own hex-byte format escapes — literal 4/4-character strings handed to `--format`, which
29
+ // git substitutes with the corresponding raw byte IN THE OUTPUT. Same technique
30
+ // `ledgerCheck.ts`'s `FORMAT_HASH_SEP`/`FORMAT_RECORD_END` use, one separator further: `%x02`
31
+ // (STX) delimits one commit's whole record (header line + following name-only/name-status
32
+ // lines) so a multi-line-per-commit `git log` call can be split back into records, `%x00` (NUL)
33
+ // delimits the two fields on a record's header line.
34
+ const FORMAT_RECORD_SEP = "%x02";
35
+ const FORMAT_FIELD_SEP = "%x00";
36
+ const RECORD_SEP = "\x02";
37
+ const FIELD_SEP = "\x00";
38
+ function repoRootOf(repo, runGit) {
39
+ const toplevel = runGit(["-C", repo, "rev-parse", "--show-toplevel"]);
40
+ return toplevel.status === 0 ? toplevel.stdout.trim() : null;
41
+ }
42
+ function daysBetween(fromDate, toDate) {
43
+ const a = Date.parse(`${fromDate}T00:00:00Z`);
44
+ const b = Date.parse(`${toDate}T00:00:00Z`);
45
+ return Math.round((b - a) / 86_400_000);
46
+ }
47
+ function median(nums) {
48
+ if (nums.length === 0)
49
+ return null;
50
+ const sorted = [...nums].sort((a, b) => a - b);
51
+ const mid = Math.floor(sorted.length / 2);
52
+ return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
53
+ }
54
+ function statsOf(nums) {
55
+ return {
56
+ count: nums.length,
57
+ medianDays: median(nums),
58
+ minDays: nums.length > 0 ? Math.min(...nums) : null,
59
+ maxDays: nums.length > 0 ? Math.max(...nums) : null,
60
+ };
61
+ }
62
+ /** One `git log --reverse` call producing one record per commit, each record's first line
63
+ * `<hash>\x00<date>` and every following non-blank line whatever `--name-only`/`--name-status`
64
+ * appended. Returns `[]` (not an error) when the call fails or the range/pathspec matches
65
+ * nothing — both routine (an empty range, a path that never existed), matching every other
66
+ * "missing is not a crash" git helper in this codebase. */
67
+ function logRecords(repoRoot, from, to, pathspecs, extraArgs, runGit) {
68
+ const log = runGit([
69
+ "-C",
70
+ repoRoot,
71
+ "log",
72
+ "--reverse",
73
+ `--format=${FORMAT_RECORD_SEP}%H${FORMAT_FIELD_SEP}%ad`,
74
+ "--date=format:%Y-%m-%d",
75
+ ...extraArgs,
76
+ `${from}..${to}`,
77
+ "--",
78
+ ...pathspecs,
79
+ ]);
80
+ if (log.status !== 0)
81
+ return [];
82
+ return log.stdout
83
+ .split(RECORD_SEP)
84
+ .map((chunk) => chunk.split("\n").filter((l) => l.length > 0))
85
+ .filter((lines) => lines.length > 0)
86
+ .map((lines) => {
87
+ const [hash, date] = lines[0].split(FIELD_SEP);
88
+ return { hash, date, lines: lines.slice(1) };
89
+ });
90
+ }
91
+ /** Slice-doc-touching commits in the range, grouped by the slice key each touched path names —
92
+ * a single `git log --name-only` walk over `slices/`, rather than one walk per key. */
93
+ function slicesTouchedByCommit(repoRoot, slicesDirAbs, from, to, runGit) {
94
+ const records = logRecords(repoRoot, from, to, [slicesDirAbs], ["--name-only"], runGit);
95
+ const byKey = new Map();
96
+ const SLICE_DOC_RE = /(?:^|\/)slices\/([^/]+)\.md$/;
97
+ for (const { hash, date, lines } of records) {
98
+ for (const path of lines) {
99
+ const m = SLICE_DOC_RE.exec(path);
100
+ if (!m)
101
+ continue;
102
+ const key = m[1];
103
+ const events = byKey.get(key) ?? [];
104
+ events.push({ commit: hash, date });
105
+ byKey.set(key, events);
106
+ }
107
+ }
108
+ return byKey;
109
+ }
110
+ /** First-appearance detector: locks onto the first commit where `curr` is non-null while the
111
+ * immediately preceding known value (`prev`) was null — returns the already-locked event on
112
+ * every subsequent call for the same accumulator. */
113
+ function firstAppearance(existing, prev, curr, commit, date) {
114
+ if (existing !== null)
115
+ return existing;
116
+ return prev === null && curr !== null ? { commit, date } : null;
117
+ }
118
+ function computeRatificationTurnaround(anchorFile, repoRoot, baseDir, from, to, runGit) {
119
+ const slicesDirAbs = resolve(baseDir, "slices");
120
+ const byKey = slicesTouchedByCommit(repoRoot, slicesDirAbs, from, to, runGit);
121
+ const keys = [...byKey.keys()].sort();
122
+ const slices = keys.map((key) => {
123
+ const baseline = resolveDocAtRevision(anchorFile, key, from, runGit);
124
+ let prevReviewed = baseline?.reviewedOn ?? null;
125
+ let prevRatified = baseline?.ratifiedOn ?? null;
126
+ let prevImplemented = baseline?.implementedIn ?? null;
127
+ let reviewedEvent = null;
128
+ let ratifiedEvent = null;
129
+ let implementedEvent = null;
130
+ for (const { commit, date } of byKey.get(key)) {
131
+ const doc = resolveDocAtRevision(anchorFile, key, commit, runGit);
132
+ if (!doc)
133
+ continue; // deleted at this commit — nothing to read, keep prior state
134
+ reviewedEvent = firstAppearance(reviewedEvent, prevReviewed, doc.reviewedOn, commit, date);
135
+ ratifiedEvent = firstAppearance(ratifiedEvent, prevRatified, doc.ratifiedOn, commit, date);
136
+ implementedEvent = firstAppearance(implementedEvent, prevImplemented, doc.implementedIn, commit, date);
137
+ prevReviewed = doc.reviewedOn;
138
+ prevRatified = doc.ratifiedOn;
139
+ prevImplemented = doc.implementedIn;
140
+ }
141
+ const reviewToRatifyDays = reviewedEvent && ratifiedEvent ? daysBetween(reviewedEvent.date, ratifiedEvent.date) : null;
142
+ const ratifyToImplementDays = ratifiedEvent && implementedEvent ? daysBetween(ratifiedEvent.date, implementedEvent.date) : null;
143
+ return { key, reviewedOn: reviewedEvent, ratifiedOn: ratifiedEvent, implementedIn: implementedEvent, reviewToRatifyDays, ratifyToImplementDays };
144
+ });
145
+ return {
146
+ slices,
147
+ reviewToRatify: statsOf(slices.map((s) => s.reviewToRatifyDays).filter((n) => n !== null)),
148
+ ratifyToImplement: statsOf(slices.map((s) => s.ratifyToImplementDays).filter((n) => n !== null)),
149
+ };
150
+ }
151
+ const REPORT_ADD_RE = /^(?:(.*)\/)?conformance\/(\d{4}-\d{2}-\d{2})-report\.md$/;
152
+ function computeConformCadence(baseDir, repoRoot, from, to, runGit) {
153
+ const conformanceDirAbs = resolve(baseDir, "conformance");
154
+ // `repoRoot` (from `git rev-parse --show-toplevel`) is already fully resolved — macOS's
155
+ // `/tmp`/`$TMPDIR` are themselves symlinks into `/private/...`, so `baseDir` needs the same
156
+ // `realpathSync` treatment before `relative()` can compare the two meaningfully; git's own
157
+ // pathspec matching (every OTHER absolute path this module hands to `git log`) already
158
+ // resolves symlinks internally, so this is the one place that needs it done explicitly.
159
+ const baseDirReal = realpathSync(resolve(baseDir));
160
+ const baseDirRepoRel = relative(repoRoot, baseDirReal).split("\\").join("/"); // "" when baseDir IS the repo root
161
+ const records = logRecords(repoRoot, from, to, [conformanceDirAbs], ["--name-status", "--diff-filter=A"], runGit);
162
+ const rows = [];
163
+ for (const { hash, lines } of records) {
164
+ for (const line of lines) {
165
+ const tab = line.indexOf("\t");
166
+ if (tab === -1)
167
+ continue;
168
+ const status = line.slice(0, tab);
169
+ const path = line.slice(tab + 1);
170
+ if (status !== "A")
171
+ continue;
172
+ const m = REPORT_ADD_RE.exec(path);
173
+ if (!m)
174
+ continue;
175
+ const prefix = m[1] ?? "";
176
+ if (prefix !== baseDirRepoRel)
177
+ continue; // a sibling model's own conformance/ report — not this model's
178
+ const relPath = baseDirRepoRel ? path.slice(baseDirRepoRel.length + 1) : path;
179
+ rows.push({ commit: hash, date: m[2], path: relPath });
180
+ }
181
+ }
182
+ // Sort by the report's own filename date (the conform run's own record of when it ran), then
183
+ // by commit hash for a deterministic tiebreak on same-day reports.
184
+ rows.sort((a, b) => (a.date === b.date ? a.commit.localeCompare(b.commit) : a.date.localeCompare(b.date)));
185
+ const entries = [];
186
+ let prevDate = null;
187
+ for (const row of rows) {
188
+ let revision = null;
189
+ let revisionSource = null;
190
+ let findingsCount = null;
191
+ let ruledCount = null;
192
+ let unruledCount = null;
193
+ const findingsRelPath = findingsPathForReport(row.path);
194
+ if (findingsRelPath) {
195
+ // Read at `to` (the range's end) rather than the adding commit — the findings record is
196
+ // sometimes filled in over one or more commits after the report itself lands; `to`
197
+ // reflects the fullest, final picture of what got ruled, not a point-in-time snapshot.
198
+ const atTo = resolveRevision(join(baseDir, findingsRelPath), to, runGit);
199
+ if (atTo.ok) {
200
+ let parsed = null;
201
+ try {
202
+ parsed = JSON.parse(atTo.content);
203
+ }
204
+ catch {
205
+ parsed = null;
206
+ }
207
+ const shape = parsed !== null ? validateFindingsShape(parsed) : { ok: false, errors: [] };
208
+ if (shape.ok) {
209
+ findingsCount = shape.doc.findings.length;
210
+ ruledCount = shape.doc.findings.filter((f) => f.locus !== null).length;
211
+ unruledCount = findingsCount - ruledCount;
212
+ if (shape.doc.revision) {
213
+ revision = shape.doc.revision;
214
+ revisionSource = "findings";
215
+ }
216
+ }
217
+ }
218
+ }
219
+ if (revision === null) {
220
+ const atCommit = resolveRevision(join(baseDir, STATE_FILE_NAME), row.commit, runGit);
221
+ if (atCommit.ok) {
222
+ const parsed = parseState(atCommit.content);
223
+ if (parsed.ok && parsed.state.lastConformance && parsed.state.lastConformance.report === row.path) {
224
+ revision = parsed.state.lastConformance.revision;
225
+ revisionSource = "state";
226
+ }
227
+ }
228
+ }
229
+ const daysSincePrevious = prevDate === null ? null : daysBetween(prevDate, row.date);
230
+ prevDate = row.date;
231
+ entries.push({ date: row.date, path: row.path, commit: row.commit, revision, revisionSource, findingsCount, ruledCount, unruledCount, daysSincePrevious });
232
+ }
233
+ return { entries, medianCadenceDays: median(entries.map((e) => e.daysSincePrevious).filter((n) => n !== null)) };
234
+ }
235
+ function countDriftAt(anchorFile, rev, runGit) {
236
+ const keys = listSliceKeysAtRevision(anchorFile, rev, runGit);
237
+ let disagreementCount = 0;
238
+ let unpropagatedCount = 0;
239
+ for (const key of keys) {
240
+ const doc = resolveDocAtRevision(anchorFile, key, rev, runGit);
241
+ if (!doc)
242
+ continue;
243
+ const signal = classifyImplementationDrift(doc);
244
+ if (signal === "implemented-without-link" || signal === "uncertified")
245
+ disagreementCount++;
246
+ else if (signal === "unpropagated-delta")
247
+ unpropagatedCount++;
248
+ }
249
+ return { disagreementCount, unpropagatedCount };
250
+ }
251
+ function computeStatusVsReality(anchorFile, baseDir, repoRoot, from, to, runGit) {
252
+ const slicesDirAbs = resolve(baseDir, "slices");
253
+ const stateFileAbs = join(baseDir, STATE_FILE_NAME);
254
+ const records = logRecords(repoRoot, from, to, [slicesDirAbs, stateFileAbs], [], runGit);
255
+ const series = records.map(({ hash, date }) => ({ commit: hash, date, ...countDriftAt(anchorFile, hash, runGit) }));
256
+ const current = countDriftAt(anchorFile, to, runGit);
257
+ return { series, current };
258
+ }
259
+ /**
260
+ * Compute `em metrics <file> --from <rev> --to <rev>` (MIL-170): the three git-history-
261
+ * computable pilot metrics, plus `readinessGateEffect: null` (Metric 4, not computable — see
262
+ * `MetricsResult`). `anchorFile` locates `slices/`/`conformance/`/`.event-modeling.md` relative
263
+ * to it, same convention `em ledger`/`em conform-scope` use — never parsed or compiled.
264
+ */
265
+ export function computeMetrics(anchorFile, from, to, runGit = realGit) {
266
+ const baseDir = dirname(resolve(anchorFile));
267
+ const repoRoot = repoRootOf(baseDir, runGit);
268
+ if (repoRoot === null) {
269
+ return { ok: false, message: `em metrics: ${anchorFile} is not inside a git repository` };
270
+ }
271
+ const fromCheck = runGit(["-C", repoRoot, "rev-parse", "--verify", `${from}^{commit}`]);
272
+ if (fromCheck.status !== 0) {
273
+ return { ok: false, message: `em metrics: unknown revision "${from}"` };
274
+ }
275
+ const toCheck = runGit(["-C", repoRoot, "rev-parse", "--verify", `${to}^{commit}`]);
276
+ if (toCheck.status !== 0) {
277
+ return { ok: false, message: `em metrics: unknown revision "${to}"` };
278
+ }
279
+ const ratificationTurnaround = computeRatificationTurnaround(anchorFile, repoRoot, baseDir, from, to, runGit);
280
+ const conformCadence = computeConformCadence(baseDir, repoRoot, from, to, runGit);
281
+ const statusVsReality = computeStatusVsReality(anchorFile, baseDir, repoRoot, from, to, runGit);
282
+ return { ok: true, result: { from, to, ratificationTurnaround, conformCadence, statusVsReality, readinessGateEffect: null } };
283
+ }
284
+ function pluralize(n, word) {
285
+ return `${n} ${word}${n === 1 ? "" : "s"}`;
286
+ }
287
+ function statsLine(s) {
288
+ if (s.count === 0)
289
+ return "no data";
290
+ return `${pluralize(s.count, "slice")}, median ${s.medianDays}d, min ${s.minDays}d, max ${s.maxDays}d`;
291
+ }
292
+ /** The `em metrics` text report — same "rollup line(s), then detail" spirit `em status`'s text
293
+ * form uses, kept plain (no table alignment) since the row counts are unbounded. */
294
+ export function formatMetricsText(m) {
295
+ const lines = [];
296
+ lines.push(`em metrics ${m.from}..${m.to}`);
297
+ lines.push("");
298
+ lines.push("Ratification turnaround:");
299
+ lines.push(` reviewed -> ratified: ${statsLine(m.ratificationTurnaround.reviewToRatify)}`);
300
+ lines.push(` ratified -> implemented: ${statsLine(m.ratificationTurnaround.ratifyToImplement)}`);
301
+ if (m.ratificationTurnaround.slices.length === 0) {
302
+ lines.push(" (no slice doc's lifecycle fields changed in this range)");
303
+ }
304
+ else {
305
+ for (const s of m.ratificationTurnaround.slices) {
306
+ const reviewed = s.reviewedOn ? s.reviewedOn.date : "—";
307
+ const ratified = s.ratifiedOn ? `${s.ratifiedOn.date} (${s.reviewToRatifyDays}d)` : "—";
308
+ const implemented = s.implementedIn ? `${s.implementedIn.date} (${s.ratifyToImplementDays}d)` : "—";
309
+ lines.push(` ${s.key}: reviewed ${reviewed}, ratified ${ratified}, implemented ${implemented}`);
310
+ }
311
+ }
312
+ lines.push("");
313
+ lines.push("Conform cadence:");
314
+ lines.push(` median cadence: ${m.conformCadence.medianCadenceDays ?? "n/a"} day(s) — ${pluralize(m.conformCadence.entries.length, "report")}`);
315
+ for (const e of m.conformCadence.entries) {
316
+ const findings = e.findingsCount === null ? "findings unknown" : `${pluralize(e.findingsCount, "finding")} (${e.ruledCount} ruled, ${e.unruledCount} unruled)`;
317
+ const cadence = e.daysSincePrevious === null ? "" : `, ${e.daysSincePrevious}d since previous`;
318
+ lines.push(` ${e.date} ${e.path} — ${findings}, revision ${e.revision ?? "unknown"}${cadence}`);
319
+ }
320
+ lines.push("");
321
+ lines.push("Status-vs-reality disagreement:");
322
+ lines.push(` current: ${pluralize(m.statusVsReality.current.disagreementCount, "disagreement")}, ${pluralize(m.statusVsReality.current.unpropagatedCount, "unpropagated-delta")}`);
323
+ lines.push(` ${pluralize(m.statusVsReality.series.length, "commit")} touched slices/state in this range`);
324
+ for (const p of m.statusVsReality.series) {
325
+ lines.push(` ${p.date} ${p.commit} — ${pluralize(p.disagreementCount, "disagreement")}, ${pluralize(p.unpropagatedCount, "unpropagated-delta")}`);
326
+ }
327
+ lines.push("");
328
+ lines.push("Readiness-gate effect: not computable from history — see docs/usage-data.md");
329
+ return lines.join("\n");
330
+ }
331
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/cli/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ import { NormalizedModel } from "../model/model.js";
2
+ import { RefsResult } from "../model/refs.js";
3
+ export declare const MODEL_VERSION_SCHEMA_VERSION = "1.0";
4
+ /** "design version N was certified at revision R on date D" (MIL-214's full, non-`--partial`
5
+ * `em state set-conformance`) — written into the CURRENT design version's manifest only.
6
+ * `report` is the conformance report path that run wrote; `findings` is the sibling findings
7
+ * JSON path beside it, or `null` when none was found (the pre-MIL-214 migration path). */
8
+ export interface ModelVersionCertified {
9
+ at: string;
10
+ on: string;
11
+ report: string;
12
+ findings: string | null;
13
+ }
14
+ /** One `model-versions/v<N>.json` document — see module header. `slices` keys are export keys
15
+ * (never re-sorted by the reader; `serializeModelVersionDoc` is the one place key order is
16
+ * made deterministic on write). */
17
+ export interface ModelVersionManifest {
18
+ modelVersionSchemaVersion: string;
19
+ /** The `.em` file's own basename — e.g. `"checkout.em"` — disambiguating within a directory
20
+ * that (like `conformance/`/`slices/`) can hold more than one model's manifests. */
21
+ model: string;
22
+ version: number;
23
+ bumpedBy: string;
24
+ bumpedOn: string;
25
+ /** The running `em`'s own `GENERATOR_VERSION` at bump time — provenance, never read back by
26
+ * `em` itself. */
27
+ emVersion: string;
28
+ modelHash: string;
29
+ slices: Record<string, number | null>;
30
+ certified: ModelVersionCertified | null;
31
+ }
32
+ /** sha256 of `source`'s bytes, after normalizing every line ending to `\n` first — so a
33
+ * CRLF-vs-LF checkout difference never reads as a content change on its own. */
34
+ export declare function computeModelHash(source: string): string;
35
+ /** Serialize a `ModelVersionManifest` deterministically — see module header. The ONE write path
36
+ * every producer of this file's bytes must go through, same discipline
37
+ * `findings.ts`'s `serializeFindingsDoc` holds. */
38
+ export declare function serializeModelVersionDoc(doc: ModelVersionManifest): string;
39
+ /** `<baseDir>/model-versions` — sibling of `slices/`/`conformance/`. `baseDir` is the `.em`
40
+ * file's directory, same convention every doc/note/manifest path in `em` uses. */
41
+ export declare function modelVersionsDir(baseDir: string): string;
42
+ export declare function modelVersionManifestPath(baseDir: string, version: number): string;
43
+ /** Every design version this model directory has ever bumped to, ascending. `[]` when
44
+ * `model-versions/` doesn't exist at all — the routine state for a model that has never
45
+ * bumped. Malformed/non-matching filenames in the directory are silently ignored (this is a
46
+ * discovery scan, not a validator). */
47
+ export declare function listModelVersionNumbers(baseDir: string): number[];
48
+ /** The highest bumped design version, or `null` when this model directory has never bumped. */
49
+ export declare function latestModelVersionNumber(baseDir: string): number | null;
50
+ export type ReadManifestResult = {
51
+ ok: true;
52
+ manifest: ModelVersionManifest;
53
+ } | {
54
+ ok: false;
55
+ message: string;
56
+ };
57
+ /** Read+parse one design version's manifest. `null` when it doesn't exist at all (not an
58
+ * error — the routine "never bumped to this version" state); `{ ok: false }` when the file
59
+ * exists but isn't readable/valid JSON (a genuine problem, reported rather than silently
60
+ * swallowed). No shape validation beyond "is it JSON" — this file is written by exactly one
61
+ * writer (`serializeModelVersionDoc`) in this codebase. */
62
+ export declare function readModelVersionManifest(baseDir: string, version: number): ReadManifestResult | null;
63
+ /** The current slices vector: every non-continuation slice's export key -> its doc's `version:`
64
+ * (or `null` when the slice has no usable doc yet). MIL-208: a continuation slice (an
65
+ * `again`-view-only slice with no legacy doc of its own) is excluded — its originating slice's
66
+ * own entry already covers it, same exclusion `conformScope.ts`'s `resolveSliceDocFacts` makes.
67
+ * `baseDir` is the `.em` file's directory, same doc-resolution convention every other doc-aware
68
+ * command uses. */
69
+ export declare function computeSlicesVector(model: NormalizedModel, refs: RefsResult, baseDir: string): Record<string, number | null>;
70
+ export type BumpModelVersionResult = {
71
+ ok: true;
72
+ version: number;
73
+ path: string;
74
+ } | {
75
+ ok: false;
76
+ message: string;
77
+ };
78
+ /**
79
+ * `em model version bump`: write `model-versions/v<N+1>.json` (N+1 = highest existing + 1, or
80
+ * 1 for the first-ever bump). Refuses when `by` is empty, and when nothing has changed since
81
+ * the latest existing manifest (same `modelHash` AND same slices vector) unless `force` — a
82
+ * no-op bump is the one case `--force` is right for (the caller wants a new version stamped
83
+ * even though nothing moved). Pure computation + one fs write; the caller is responsible for
84
+ * the state-file-missing precondition (a model-version bump without a resumable state file to
85
+ * point the `Model version:` bullet at makes no sense — `em model version bump`'s CLI action
86
+ * checks that first, before this function is ever called) and for rewriting the state file's
87
+ * own `Model version:` bullet afterward (`stateFile.ts`'s `setModelVersion`).
88
+ */
89
+ export declare function runModelVersionBump(baseDir: string, modelFileName: string, model: NormalizedModel, refs: RefsResult, source: string, by: string, on: string, emVersion: string, force: boolean): BumpModelVersionResult;
90
+ export interface ModelVersionSliceChange {
91
+ key: string;
92
+ from: number | null;
93
+ to: number | null;
94
+ }
95
+ /** The warn-only "has the vector moved since the last bump" fact — never a refusal, every
96
+ * caller (`em status`, `em validate`'s `model-version-stale`, the ratify/reratify advisory)
97
+ * decides how to surface it. `current: null` (never bumped) always reads as no drift — a repo
98
+ * that never bumped a design version has nothing to be stale against. A manifest that exists
99
+ * but fails to read/parse is treated the same conservative way: no drift reported (the file's
100
+ * own problem is a `readModelVersionManifest` concern for a caller that wants to surface it,
101
+ * not this predicate's).
102
+ */
103
+ export interface ModelVersionDrift {
104
+ current: number | null;
105
+ hashChanged: boolean;
106
+ slicesChanged: ModelVersionSliceChange[];
107
+ }
108
+ export declare function modelVersionDrift(baseDir: string, model: NormalizedModel, refs: RefsResult, source: string): ModelVersionDrift;
109
+ /** The most recently bumped design version that has ever been certified — walking every
110
+ * manifest newest-first, since a later bump doesn't clear an earlier version's own
111
+ * `certified` stamp (only the CURRENT design version's manifest is ever written to by `em
112
+ * state set-conformance`; older manifests keep whatever certification they already carried).
113
+ * `null` when no manifest has ever been certified. Malformed manifests are skipped (best
114
+ * effort, same posture `findings.ts`'s `listAllFindingsFiles` takes toward a broken file it
115
+ * isn't itself the loud diagnostic for). */
116
+ export declare function findCertifiedVersion(baseDir: string): {
117
+ version: number;
118
+ certified: ModelVersionCertified;
119
+ } | null;
120
+ export type CertifyModelVersionResult = {
121
+ ok: true;
122
+ version: number;
123
+ path: string;
124
+ } | {
125
+ ok: false;
126
+ message: string;
127
+ };
128
+ /**
129
+ * `em state set-conformance` (non-`--partial`, MIL-218 ruling D): write `certified: { at, on,
130
+ * report, findings }` into the CURRENT design version's manifest. Refuses when no manifest
131
+ * exists yet ("bump a model version first") and when the vector has drifted since that
132
+ * manifest was bumped (`modelVersionDrift`, computed here — the question is exactly "has THIS
133
+ * model changed since v<N> was bumped", the same predicate every other warn-only surface
134
+ * reads): the certification would otherwise name a version that isn't what was actually
135
+ * checked. Idempotent on an identical `certified` value; a DIFFERENT one simply overwrites —
136
+ * re-certifying at a later revision is legal and common (MIL-214's own `em slice conform` holds
137
+ * the same "overwrite, no --force" posture for exactly this reason).
138
+ */
139
+ export declare function runCertifyModelVersion(baseDir: string, model: NormalizedModel, refs: RefsResult, source: string, at: string, on: string, report: string, findings: string | null): CertifyModelVersionResult;