@milehimikey/em 1.8.1 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/event-modeling/SKILL.md +58 -404
- package/.claude/skills/event-modeling-conform/SKILL.md +77 -0
- package/.claude/skills/{event-modeling → event-modeling-conform}/reference/conform.md +40 -14
- package/.claude/skills/event-modeling-design/SKILL.md +139 -0
- package/.claude/skills/event-modeling-discover/SKILL.md +89 -0
- package/.claude/skills/{event-modeling → event-modeling-discover}/reference/extract.md +3 -2
- package/.claude/skills/event-modeling-implement/SKILL.md +40 -0
- package/.claude/skills/{event-modeling → event-modeling-implement}/reference/implement.md +3 -2
- package/.claude/skills/event-modeling-review/SKILL.md +58 -0
- package/.claude/skills/{event-modeling → event-modeling-shared}/reference/em-dsl.md +115 -83
- package/.claude/skills/{event-modeling → event-modeling-shared}/reference/methodology.md +6 -5
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +150 -0
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +381 -0
- package/.claude/skills/{event-modeling → event-modeling-shared}/templates/model-readme.md +2 -2
- package/.claude/skills/{event-modeling → event-modeling-shared}/templates/slice.md +57 -14
- package/README.md +24 -4
- package/dist/catalog/build.js +9 -0
- package/dist/catalog/build.js.map +1 -1
- package/dist/catalog/docJoin.d.ts +15 -0
- package/dist/catalog/docJoin.js +8 -0
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/modelCollisionValidate.d.ts +22 -0
- package/dist/catalog/modelCollisionValidate.js +67 -0
- package/dist/catalog/modelCollisionValidate.js.map +1 -0
- package/dist/catalog/orphanedSliceDocValidate.d.ts +11 -0
- package/dist/catalog/orphanedSliceDocValidate.js +98 -0
- package/dist/catalog/orphanedSliceDocValidate.js.map +1 -0
- package/dist/catalog/sliceDoc.d.ts +15 -0
- package/dist/catalog/sliceDoc.js +18 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/cli/changelogBuild.d.ts +12 -0
- package/dist/cli/changelogBuild.js +89 -0
- package/dist/cli/changelogBuild.js.map +1 -0
- package/dist/cli/ciInit.d.ts +42 -0
- package/dist/cli/ciInit.js +298 -0
- package/dist/cli/ciInit.js.map +1 -0
- package/dist/cli/conformSupersede.d.ts +32 -0
- package/dist/cli/conformSupersede.js +102 -0
- package/dist/cli/conformSupersede.js.map +1 -0
- package/dist/cli/coverage.d.ts +6 -1
- package/dist/cli/coverage.js +28 -3
- package/dist/cli/coverage.js.map +1 -1
- package/dist/cli/frontmatterSurgery.d.ts +28 -0
- package/dist/cli/frontmatterSurgery.js +66 -0
- package/dist/cli/frontmatterSurgery.js.map +1 -0
- package/dist/cli/markImplemented.js +6 -45
- package/dist/cli/markImplemented.js.map +1 -1
- package/dist/cli/ratify.d.ts +41 -0
- package/dist/cli/ratify.js +151 -0
- package/dist/cli/ratify.js.map +1 -0
- package/dist/cli/reratify.d.ts +33 -0
- package/dist/cli/reratify.js +137 -0
- package/dist/cli/reratify.js.map +1 -0
- package/dist/cli/skillCheck.d.ts +6 -0
- package/dist/cli/skillCheck.js +46 -0
- package/dist/cli/skillCheck.js.map +1 -1
- package/dist/cli/skillDirs.d.ts +15 -0
- package/dist/cli/skillDirs.js +34 -0
- package/dist/cli/skillDirs.js.map +1 -0
- package/dist/cli/skillSync.d.ts +10 -0
- package/dist/cli/skillSync.js +15 -0
- package/dist/cli/skillSync.js.map +1 -1
- package/dist/cli/sliceIndex.d.ts +3 -0
- package/dist/cli/sliceIndex.js +6 -2
- package/dist/cli/sliceIndex.js.map +1 -1
- package/dist/cli/sliceLink.d.ts +52 -0
- package/dist/cli/sliceLink.js +152 -0
- package/dist/cli/sliceLink.js.map +1 -0
- package/dist/cli/status.d.ts +261 -0
- package/dist/cli/status.js +536 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/usageLog.d.ts +75 -0
- package/dist/cli/usageLog.js +193 -0
- package/dist/cli/usageLog.js.map +1 -0
- package/dist/cli.js +503 -132
- package/dist/cli.js.map +1 -1
- package/dist/emit/freshnessJson.d.ts +6 -0
- package/dist/emit/freshnessJson.js +24 -0
- package/dist/emit/freshnessJson.js.map +1 -0
- package/dist/emit/json.d.ts +62 -8
- package/dist/emit/json.js +14 -3
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/statusJson.d.ts +7 -0
- package/dist/emit/statusJson.js +39 -0
- package/dist/emit/statusJson.js.map +1 -0
- package/dist/emit/typespec.d.ts +26 -0
- package/dist/emit/typespec.js +356 -0
- package/dist/emit/typespec.js.map +1 -0
- package/dist/emit/usageReportJson.d.ts +3 -0
- package/dist/emit/usageReportJson.js +23 -0
- package/dist/emit/usageReportJson.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +301 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/rules.d.ts +13 -0
- package/dist/model/rules.js +20 -0
- package/dist/model/rules.js.map +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +2 -2
- package/dist/util/markers.d.ts +16 -4
- package/dist/util/markers.js +23 -10
- package/dist/util/markers.js.map +1 -1
- package/package.json +2 -1
- /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/conformance-report.md +0 -0
- /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/state.md +0 -0
|
@@ -49,6 +49,21 @@ export interface SliceDocExport {
|
|
|
49
49
|
* `version` above from the same doc parse — a finding built from `driftSignal` should always
|
|
50
50
|
* cite `version` alongside it, never cache one without the other. */
|
|
51
51
|
driftSignal: DriftSignalKind | null;
|
|
52
|
+
/** MIL-165: who ratified this doc's current `status`/`version` (frontmatter `ratifiedBy:`),
|
|
53
|
+
* written only by `em slice ratify` — null when absent (a doc predating this feature, or
|
|
54
|
+
* ratified by hand before it existed). */
|
|
55
|
+
ratifiedBy: string | null;
|
|
56
|
+
/** MIL-165: when this doc was ratified (frontmatter `ratifiedOn:`, `YYYY-MM-DD`), written only
|
|
57
|
+
* by `em slice ratify` — null when absent, same as `ratifiedBy`. */
|
|
58
|
+
ratifiedOn: string | null;
|
|
59
|
+
/** MIL-171: who (a person or team) holds this slice, from frontmatter `owner:` — hand-filled,
|
|
60
|
+
* no `em` command writes it. Null when absent. */
|
|
61
|
+
owner: string | null;
|
|
62
|
+
/** MIL-171: a URL into an external tracker mirroring this slice, from frontmatter `tracking:`
|
|
63
|
+
* — hand-filled, no `em` command writes it. Null when absent. This is the exact field
|
|
64
|
+
* em-tracker-bridge reads to find the mirrored ticket — its name/shape here is a cross-tool
|
|
65
|
+
* contract, not just an internal display field. */
|
|
66
|
+
tracking: string | null;
|
|
52
67
|
}
|
|
53
68
|
export interface SliceDocJoinResult {
|
|
54
69
|
doc: SliceDocExport;
|
package/dist/catalog/docJoin.js
CHANGED
|
@@ -66,6 +66,10 @@ const EMPTY_CONTENT = {
|
|
|
66
66
|
mergedFrom: [],
|
|
67
67
|
supersededBy: [],
|
|
68
68
|
driftSignal: null,
|
|
69
|
+
ratifiedBy: null,
|
|
70
|
+
ratifiedOn: null,
|
|
71
|
+
owner: null,
|
|
72
|
+
tracking: null,
|
|
69
73
|
};
|
|
70
74
|
/**
|
|
71
75
|
* Resolve one slice's doc join. `baseDir` is the `.em` file's directory (doc paths, like note
|
|
@@ -150,6 +154,10 @@ function foundDoc(path, parsed) {
|
|
|
150
154
|
mergedFrom: parsed.mergedFrom,
|
|
151
155
|
supersededBy: parsed.supersededBy,
|
|
152
156
|
driftSignal: classifyImplementationDrift(parsed),
|
|
157
|
+
ratifiedBy: parsed.ratifiedBy,
|
|
158
|
+
ratifiedOn: parsed.ratifiedOn,
|
|
159
|
+
owner: parsed.owner,
|
|
160
|
+
tracking: parsed.tracking,
|
|
153
161
|
};
|
|
154
162
|
}
|
|
155
163
|
//# sourceMappingURL=docJoin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docJoin.js","sourceRoot":"","sources":["../../src/catalog/docJoin.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,4FAA4F;AAC5F,6FAA6F;AAC7F,2FAA2F;AAC3F,4FAA4F;AAC5F,uFAAuF;AACvF,6FAA6F;AAC7F,oFAAoF;AACpF,0FAA0F;AAC1F,4FAA4F;AAC5F,EAAE;AACF,oFAAoF;AACpF,6FAA6F;AAC7F,4FAA4F;AAC5F,8FAA8F;AAC9F,wCAAwC;AACxC,EAAE;AACF,gGAAgG;AAChG,gGAAgG;AAChG,gGAAgG;AAChG,8FAA8F;AAC9F,iGAAiG;AACjG,6FAA6F;AAC7F,+FAA+F;AAC/F,iGAAiG;AACjG,6FAA6F;AAC7F,gGAAgG;AAChG,oDAAoD;AACpD,EAAE;AACF,6FAA6F;AAC7F,qEAAqE;AACrE,2FAA2F;AAC3F,4FAA4F;AAC5F,iGAAiG;AACjG,0FAA0F;AAC1F,oFAAoF;AAIpF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,2BAA2B,EAAmB,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAsB,MAAM,eAAe,CAAC;AAEzE,0FAA0F;AAC1F,oGAAoG;AACpG,0DAA0D;AAC1D,MAAM,CAAC,MAAM,eAAe,GAAG,2CAA2C,CAAC;AAY3E;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,YAAoB,EAAE,QAAgB;IAC3F,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACvC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC9D,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACpE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;AACrC,CAAC;
|
|
1
|
+
{"version":3,"file":"docJoin.js","sourceRoot":"","sources":["../../src/catalog/docJoin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Diagnostic } from "../model/validate.js";
|
|
2
|
+
export interface ModelSliceKeys {
|
|
3
|
+
/** The `.em` file as given on the command line — same identity every other multi-model
|
|
4
|
+
* diagnostic (StatusDiagnostic, CatalogBuildResult.diagnostics) tags itself with. */
|
|
5
|
+
file: string;
|
|
6
|
+
/** This model's own compiled slice export keys, same order/index as `computeRefs()`'s
|
|
7
|
+
* `sliceKeys` — duplicates already deduped with a `~2` suffix by computeRefs() itself, so a
|
|
8
|
+
* key here is always a real, single doc path this model expects to read/write. */
|
|
9
|
+
sliceKeys: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Detect colliding `slices/<key>.md` doc paths across `models`: any two files whose resolved
|
|
13
|
+
* base directory (`dirname(file)`, absolute so `./a.em` and `a.em` from the same cwd count as
|
|
14
|
+
* the same directory) is identical AND who share at least one slice export key. One diagnostic
|
|
15
|
+
* per colliding key, attributed to the SECOND model to use that key in file-list order — same
|
|
16
|
+
* "first wins, the later one gets flagged" convention `computeRefs()`'s own `duplicate-slice-name`
|
|
17
|
+
* diagnostic uses, so a collision reads as "this model's key collides with an earlier one," not
|
|
18
|
+
* the reverse. A model list of 0 or 1 files (or where every file resolves to a distinct
|
|
19
|
+
* directory) always returns `[]` — the ordinary single-model-per-directory case this function
|
|
20
|
+
* exists to leave alone.
|
|
21
|
+
*/
|
|
22
|
+
export declare function detectSliceDocCollisions(models: ModelSliceKeys[]): Diagnostic[];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Cross-model slice-doc collision detection (MIL-160). readSliceDoc.ts resolves every slice doc
|
|
3
|
+
// at `<dirname(modelFile)>/slices/<sliceKey>.md` — safe as long as every model owns its own
|
|
4
|
+
// directory (the documented convention, docs/cli.md "Multi-model projects"), since computeRefs()
|
|
5
|
+
// only dedupes a slice key against the OTHER slices in the SAME model's compile
|
|
6
|
+
// (src/model/refs.ts). Two sibling `.em` files sharing a directory, each producing a slice
|
|
7
|
+
// keyed e.g. "checkout", would both resolve to the same `slices/checkout.md` — silently: neither
|
|
8
|
+
// model's compile ever sees the other, so nothing raises a diagnostic today. This is the one
|
|
9
|
+
// check that does: given every input model's file path and its own compiled slice keys, group by
|
|
10
|
+
// resolved base directory and flag any key more than one model in the same group produces.
|
|
11
|
+
//
|
|
12
|
+
// The only cross-model rule in the codebase — every other rule in model/rules.ts is a
|
|
13
|
+
// single-model concern raised from inside one compile. Deliberately NOT part of
|
|
14
|
+
// model/validate.ts's pipeline (which only ever sees one model at a time); instead wired
|
|
15
|
+
// directly into the two commands that ever compile more than one model in the same run
|
|
16
|
+
// (`em status` in src/cli.ts, `em catalog` in src/catalog/build.ts) — the only places that
|
|
17
|
+
// could possibly witness a collision. Pure — no fs — the compiled `sliceKeys` are handed in by
|
|
18
|
+
// the caller, already computed by the same computeRefs() call the caller made anyway.
|
|
19
|
+
import { dirname, resolve } from "node:path";
|
|
20
|
+
import { makeDiag } from "../model/rules.js";
|
|
21
|
+
/**
|
|
22
|
+
* Detect colliding `slices/<key>.md` doc paths across `models`: any two files whose resolved
|
|
23
|
+
* base directory (`dirname(file)`, absolute so `./a.em` and `a.em` from the same cwd count as
|
|
24
|
+
* the same directory) is identical AND who share at least one slice export key. One diagnostic
|
|
25
|
+
* per colliding key, attributed to the SECOND model to use that key in file-list order — same
|
|
26
|
+
* "first wins, the later one gets flagged" convention `computeRefs()`'s own `duplicate-slice-name`
|
|
27
|
+
* diagnostic uses, so a collision reads as "this model's key collides with an earlier one," not
|
|
28
|
+
* the reverse. A model list of 0 or 1 files (or where every file resolves to a distinct
|
|
29
|
+
* directory) always returns `[]` — the ordinary single-model-per-directory case this function
|
|
30
|
+
* exists to leave alone.
|
|
31
|
+
*/
|
|
32
|
+
export function detectSliceDocCollisions(models) {
|
|
33
|
+
const diagnostics = [];
|
|
34
|
+
const byDir = new Map();
|
|
35
|
+
for (const m of models) {
|
|
36
|
+
const dir = resolve(dirname(m.file));
|
|
37
|
+
const bucket = byDir.get(dir);
|
|
38
|
+
if (bucket)
|
|
39
|
+
bucket.push(m);
|
|
40
|
+
else
|
|
41
|
+
byDir.set(dir, [m]);
|
|
42
|
+
}
|
|
43
|
+
for (const bucket of byDir.values()) {
|
|
44
|
+
if (bucket.length < 2)
|
|
45
|
+
continue; // only one model in this directory — no collision possible
|
|
46
|
+
const firstFileForKey = new Map();
|
|
47
|
+
for (const m of bucket) {
|
|
48
|
+
for (const key of m.sliceKeys) {
|
|
49
|
+
const earlier = firstFileForKey.get(key);
|
|
50
|
+
if (earlier === undefined) {
|
|
51
|
+
firstFileForKey.set(key, m.file);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (earlier === m.file)
|
|
55
|
+
continue; // same model listed twice on the command line
|
|
56
|
+
diagnostics.push(makeDiag("cross-model-slice-doc-collision", {
|
|
57
|
+
message: `"${m.file}" and "${earlier}" share a directory and both produce slice key "${key}" — ` +
|
|
58
|
+
`both would read/write "slices/${key}.md"; give each model its own directory ` +
|
|
59
|
+
`(see docs/cli.md, "Multi-model projects")`,
|
|
60
|
+
refs: [key, earlier, m.file],
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return diagnostics;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=modelCollisionValidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelCollisionValidate.js","sourceRoot":"","sources":["../../src/catalog/modelCollisionValidate.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,gGAAgG;AAChG,4FAA4F;AAC5F,iGAAiG;AACjG,gFAAgF;AAChF,2FAA2F;AAC3F,iGAAiG;AACjG,6FAA6F;AAC7F,iGAAiG;AACjG,2FAA2F;AAC3F,EAAE;AACF,sFAAsF;AACtF,gFAAgF;AAChF,yFAAyF;AACzF,uFAAuF;AACvF,2FAA2F;AAC3F,+FAA+F;AAC/F,sFAAsF;AAEtF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAY7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAwB;IAC/D,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;YACtB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS,CAAC,2DAA2D;QAC5F,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;QAClD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACzC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;oBAC1B,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;oBACjC,SAAS;gBACX,CAAC;gBACD,IAAI,OAAO,KAAK,CAAC,CAAC,IAAI;oBAAE,SAAS,CAAC,8CAA8C;gBAChF,WAAW,CAAC,IAAI,CACd,QAAQ,CAAC,iCAAiC,EAAE;oBAC1C,OAAO,EACL,IAAI,CAAC,CAAC,IAAI,UAAU,OAAO,mDAAmD,GAAG,MAAM;wBACvF,iCAAiC,GAAG,0CAA0C;wBAC9E,2CAA2C;oBAC7C,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC;iBAC7B,CAAC,CACH,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NormalizedModel } from "../model/model.js";
|
|
2
|
+
import { RefsResult } from "../model/refs.js";
|
|
3
|
+
import { Diagnostic } from "../model/validate.js";
|
|
4
|
+
/**
|
|
5
|
+
* Scan `<baseDir>/slices/` for a doc file that no current slice's own key names by filename
|
|
6
|
+
* convention, and whose `covers:` list (if any — MIL-121) doesn't ratify a current slice either.
|
|
7
|
+
* `baseDir` is the `.em` file's directory, same convention every other doc/note path in em uses.
|
|
8
|
+
* Returns `[]` when `slices/` doesn't exist at all — nothing to a scan with no directory to walk,
|
|
9
|
+
* the ordinary state for a model that hasn't authored any slice docs yet.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateOrphanedSliceDocs(_model: NormalizedModel, refs: RefsResult, baseDir: string): Diagnostic[];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// `em validate`'s fifth fs-aware rule (MIL-183, the fragility half of GH #128): a slice doc left
|
|
3
|
+
// behind in `slices/` after its slice is renamed or removed used to just quietly stop applying —
|
|
4
|
+
// nothing ever pointed at the ORPHANED FILE itself. Same fs-aware-sibling shape as
|
|
5
|
+
// catalog/lineageValidate.ts (MIL-84), catalog/frontmatterCoherenceValidate.ts (MIL-85),
|
|
6
|
+
// catalog/noteBindingValidate.ts (MIL-126), and catalog/docModelConsistencyValidate.ts (MIL-124):
|
|
7
|
+
// a module next to model/validate.ts, not folded into it, because it needs `baseDir`/fs access
|
|
8
|
+
// the rest of validate deliberately never touches. Distinct from all four of those, though, in
|
|
9
|
+
// which direction it walks: every one of them starts from a *slice* and asks "is its doc/note
|
|
10
|
+
// okay?" — this is the only rule that instead walks the filesystem and asks the opposite
|
|
11
|
+
// direction, "does every file out there still belong to something?"
|
|
12
|
+
//
|
|
13
|
+
// Two doc-join mechanisms coexist in this codebase (docJoin.ts's own header comment spells out
|
|
14
|
+
// the same split): `docJoin.ts`'s `resolveSliceDocJoin`, gated on an explicit
|
|
15
|
+
// `note "slices/<key>.md"` binding, drives `em export`/`em status`/the MCP tools; `em catalog`
|
|
16
|
+
// (catalog/build.ts) instead reads `slices/<sliceKey>.md` by BARE FILENAME CONVENTION, ignoring
|
|
17
|
+
// `note` entirely — a standing, deliberately-kept-separate, tested invariant (see build.ts's own
|
|
18
|
+
// header comment and test/catalog.e2e.test.ts). The GH #128 reporter's "matched by name, silently
|
|
19
|
+
// stops applying" experience is build.ts's convention path, not docJoin's note-gated one — status
|
|
20
|
+
// coloring on the diagram comes from whether a file happens to sit at the right name, full stop.
|
|
21
|
+
// So "orphaned" here is judged the same name-convention way build.ts resolves a doc: by filename
|
|
22
|
+
// key (and, for a MIL-121 covering doc, by its `covers:` list), never by whether some element
|
|
23
|
+
// happens to carry a `note`. A note-binding problem (a note pointing at the wrong place) is
|
|
24
|
+
// noteBindingValidate.ts's business; a stale file nothing names by convention or `covers:` any
|
|
25
|
+
// more is this rule's.
|
|
26
|
+
//
|
|
27
|
+
// A file only counts as a slice doc worth checking when its frontmatter is USABLE
|
|
28
|
+
// (hasUsableFrontmatter — sliceDoc.ts, the exact gate `em export`'s `frontmatter-invalid` and
|
|
29
|
+
// every sibling module already use, so this rule can't silently disagree with them on what counts
|
|
30
|
+
// as a readable doc). This is what keeps a README, a scratch note, or any other non-doc file in
|
|
31
|
+
// `slices/` from ever being flagged: nothing here can distinguish such a file from "meant to
|
|
32
|
+
// become a slice doc but hasn't been fleshed out yet" except the file declaring itself one via
|
|
33
|
+
// frontmatter — the same declare-gating posture docModelConsistencyValidate.ts's checks take.
|
|
34
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
35
|
+
import { join } from "node:path";
|
|
36
|
+
import { pushDiag } from "../model/rules.js";
|
|
37
|
+
import { hasUsableFrontmatter, parseSliceDoc } from "./sliceDoc.js";
|
|
38
|
+
/**
|
|
39
|
+
* Scan `<baseDir>/slices/` for a doc file that no current slice's own key names by filename
|
|
40
|
+
* convention, and whose `covers:` list (if any — MIL-121) doesn't ratify a current slice either.
|
|
41
|
+
* `baseDir` is the `.em` file's directory, same convention every other doc/note path in em uses.
|
|
42
|
+
* Returns `[]` when `slices/` doesn't exist at all — nothing to a scan with no directory to walk,
|
|
43
|
+
* the ordinary state for a model that hasn't authored any slice docs yet.
|
|
44
|
+
*/
|
|
45
|
+
export function validateOrphanedSliceDocs(_model, refs, baseDir) {
|
|
46
|
+
// `_model` is unused — `refs.sliceKeys` already carries every current slice's key. Kept in the
|
|
47
|
+
// signature anyway to match every sibling fs-aware rule's `(model, refs, baseDir)` shape, so
|
|
48
|
+
// every call site can pass the same three arguments uniformly.
|
|
49
|
+
const slicesDir = join(baseDir, "slices");
|
|
50
|
+
let entries;
|
|
51
|
+
try {
|
|
52
|
+
entries = readdirSync(slicesDir, { withFileTypes: true })
|
|
53
|
+
.filter((e) => e.isFile())
|
|
54
|
+
.map((e) => e.name);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return []; // no slices/ directory (or unreadable) — nothing to orphan
|
|
58
|
+
}
|
|
59
|
+
const liveKeys = new Set(refs.sliceKeys.map((k) => k.toLowerCase()));
|
|
60
|
+
const diags = [];
|
|
61
|
+
for (const entry of entries.slice().sort()) {
|
|
62
|
+
if (!entry.toLowerCase().endsWith(".md"))
|
|
63
|
+
continue; // not a slice-doc-shaped file at all
|
|
64
|
+
const key = entry.slice(0, -".md".length);
|
|
65
|
+
if (liveKeys.has(key.toLowerCase()))
|
|
66
|
+
continue; // matches a current slice's key by name — never orphaned
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = parseSliceDoc(readFileSync(join(slicesDir, entry), "utf8"));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue; // unreadable file (permissions, ...) — not this rule's business
|
|
73
|
+
}
|
|
74
|
+
// Gate on usable frontmatter, same predicate every sibling fs-aware rule uses: a README, a
|
|
75
|
+
// freeform note, or a draft that never got as far as a full frontmatter block is not
|
|
76
|
+
// confidently a slice doc at all, so it's never flagged as one gone stale.
|
|
77
|
+
if (!hasUsableFrontmatter(parsed))
|
|
78
|
+
continue;
|
|
79
|
+
// MIL-121: a doc whose own canonical slice is gone can still legitimately serve a DIFFERENT,
|
|
80
|
+
// still-live slice via `covers:` — the two-slice Automation/Translation shape's shared-doc
|
|
81
|
+
// case. Ratifying coverage of any current slice is enough; this mirrors docJoin.ts's own
|
|
82
|
+
// predicate loosely (it doesn't require an actual winning cross-note here, only that the
|
|
83
|
+
// `covers:` declaration itself still names something live) — a stricter "and some note also
|
|
84
|
+
// points here" check would reintroduce exactly the kind of false positive this ticket calls
|
|
85
|
+
// out to avoid.
|
|
86
|
+
if (parsed.covers.some((c) => liveKeys.has(c)))
|
|
87
|
+
continue;
|
|
88
|
+
pushDiag(diags, "orphaned-slice-doc", {
|
|
89
|
+
message: `slice doc "slices/${entry}" matches no current slice's key, and its \`covers:\` list ` +
|
|
90
|
+
`(if any) doesn't name one either — likely left behind by a rename or removal. Rename it ` +
|
|
91
|
+
`to the renamed slice's key, add \`covers: <slice-key>\` (plus a \`note\` binding) to ` +
|
|
92
|
+
`attach it to a live slice, or delete it if it's no longer needed`,
|
|
93
|
+
refs: [key],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return diags;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=orphanedSliceDocValidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orphanedSliceDocValidate.js","sourceRoot":"","sources":["../../src/catalog/orphanedSliceDocValidate.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,iGAAiG;AACjG,iGAAiG;AACjG,mFAAmF;AACnF,yFAAyF;AACzF,kGAAkG;AAClG,+FAA+F;AAC/F,+FAA+F;AAC/F,8FAA8F;AAC9F,yFAAyF;AACzF,oEAAoE;AACpE,EAAE;AACF,+FAA+F;AAC/F,8EAA8E;AAC9E,+FAA+F;AAC/F,gGAAgG;AAChG,iGAAiG;AACjG,kGAAkG;AAClG,kGAAkG;AAClG,iGAAiG;AACjG,iGAAiG;AACjG,8FAA8F;AAC9F,4FAA4F;AAC5F,+FAA+F;AAC/F,uBAAuB;AACvB,EAAE;AACF,kFAAkF;AAClF,8FAA8F;AAC9F,kGAAkG;AAClG,gGAAgG;AAChG,6FAA6F;AAC7F,+FAA+F;AAC/F,8FAA8F;AAE9F,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAuB,EAAE,IAAgB,EAAE,OAAe;IAClG,+FAA+F;IAC/F,6FAA6F;IAC7F,+DAA+D;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1C,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACtD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,2DAA2D;IACxE,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACrE,MAAM,KAAK,GAAiB,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3C,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS,CAAC,qCAAqC;QACzF,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAAE,SAAS,CAAC,yDAAyD;QAExG,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,gEAAgE;QAC5E,CAAC;QACD,2FAA2F;QAC3F,qFAAqF;QACrF,2EAA2E;QAC3E,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC;YAAE,SAAS;QAC5C,6FAA6F;QAC7F,2FAA2F;QAC3F,yFAAyF;QACzF,yFAAyF;QACzF,4FAA4F;QAC5F,4FAA4F;QAC5F,gBAAgB;QAChB,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAAE,SAAS;QAEzD,QAAQ,CAAC,KAAK,EAAE,oBAAoB,EAAE;YACpC,OAAO,EACL,qBAAqB,KAAK,6DAA6D;gBACvF,0FAA0F;gBAC1F,uFAAuF;gBACvF,kEAAkE;YACpE,IAAI,EAAE,CAAC,GAAG,CAAC;SACZ,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -59,6 +59,21 @@ export interface SliceDoc {
|
|
|
59
59
|
* the noting slice back. Empty array when absent — the common case; a doc always covers its
|
|
60
60
|
* own canonical slice key implicitly and never needs to list it here. */
|
|
61
61
|
covers: string[];
|
|
62
|
+
/** MIL-165: `ratifiedBy:` — free text, typically a person's name — or null when absent.
|
|
63
|
+
* Written only by `em slice ratify`; no legacy bullet-line form. */
|
|
64
|
+
ratifiedBy: string | null;
|
|
65
|
+
/** MIL-165: `ratifiedOn:` — a `YYYY-MM-DD` date string, or null when absent. Written only by
|
|
66
|
+
* `em slice ratify`; validated by the CLI layer, not re-validated here (this parser stays as
|
|
67
|
+
* lenient about value shape as every other frontmatter field). */
|
|
68
|
+
ratifiedOn: string | null;
|
|
69
|
+
/** MIL-171: `owner:` — free text, typically a person or team name — or null when absent.
|
|
70
|
+
* Hand-filled; no `em` command writes it. */
|
|
71
|
+
owner: string | null;
|
|
72
|
+
/** MIL-171: `tracking:` — free text, typically an external ticket/issue URL — or null when
|
|
73
|
+
* absent. Hand-filled; no `em` command writes it. This is the field em-tracker-bridge reads
|
|
74
|
+
* to find the ticket mirroring this slice — its export name/shape (docJoin.ts) is a
|
|
75
|
+
* cross-tool contract, not just an internal display field. */
|
|
76
|
+
tracking: string | null;
|
|
62
77
|
/** True when a well-formed leading `---`/`---` frontmatter fence was found and
|
|
63
78
|
* closed — independent of which keys it contained. False for a legacy
|
|
64
79
|
* status-bullet-only doc, a doc with no frontmatter at all, or an
|
package/dist/catalog/sliceDoc.js
CHANGED
|
@@ -33,6 +33,20 @@
|
|
|
33
33
|
// `note "slices/<this-key>.md"` instead of authoring its own. Parsed the same
|
|
34
34
|
// comma-separated-list shape as `merged-from`/`superseded-by`; see docJoin.ts for the
|
|
35
35
|
// two-ended handshake that actually uses it.
|
|
36
|
+
//
|
|
37
|
+
// `ratifiedBy`/`ratifiedOn` (MIL-165) are a fifth and sixth optional frontmatter-only field:
|
|
38
|
+
// who ratified this doc's current `status`/`version`, and when, written by `em slice ratify`
|
|
39
|
+
// (cli/ratify.ts) alone — same "one write path" discipline `implementedIn` has with `em slice
|
|
40
|
+
// mark-implemented`. Additive, tolerate-unknown-fields: a doc that predates this feature (or was
|
|
41
|
+
// hand-ratified) simply has neither key, same as any other optional field's absence.
|
|
42
|
+
//
|
|
43
|
+
// `owner`/`tracking` (MIL-171) are a seventh and eighth optional frontmatter-only field: who
|
|
44
|
+
// (a person or team) holds this slice, and a URL into an external tracker mirroring it. Unlike
|
|
45
|
+
// `ratifiedBy`/`ratifiedOn`, neither has a dedicated `em` write path — both are hand-filled, free
|
|
46
|
+
// text, exactly like `implementedIn`. `em` only stores and displays them; `tracking` in
|
|
47
|
+
// particular is the field em-tracker-bridge reads to find the mirrored ticket, so its export
|
|
48
|
+
// name/shape is a cross-tool contract — see docJoin.ts/emit/json.ts. Additive,
|
|
49
|
+
// tolerate-unknown-fields: absent on every doc predating this feature.
|
|
36
50
|
import { marked } from "marked";
|
|
37
51
|
/** Frontmatter keys docs/slice-doc-schema.md's required-vs-optional table marks
|
|
38
52
|
* required at every `status` — the mechanical basis for `frontmatter-invalid`
|
|
@@ -182,6 +196,10 @@ export function parseSliceDoc(markdown) {
|
|
|
182
196
|
supersededBy: parseSliceRefList(fields.get("superseded-by")),
|
|
183
197
|
implementedIn: fields.get("implementedin") ?? null,
|
|
184
198
|
covers: parseKeyList(fields.get("covers")),
|
|
199
|
+
ratifiedBy: fields.get("ratifiedby") ?? null,
|
|
200
|
+
ratifiedOn: fields.get("ratifiedon") ?? null,
|
|
201
|
+
owner: fields.get("owner") ?? null,
|
|
202
|
+
tracking: fields.get("tracking") ?? null,
|
|
185
203
|
frontmatterPresent,
|
|
186
204
|
missingRequiredFields: REQUIRED_FRONTMATTER_KEYS.filter((k) => !fields.has(k)),
|
|
187
205
|
html: marked.parse(body, { async: false }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sliceDoc.js","sourceRoot":"","sources":["../../src/catalog/sliceDoc.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,yDAAyD;AACzD,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAC9E,iEAAiE;AACjE,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,8EAA8E;AAC9E,2EAA2E;AAC3E,yEAAyE;AACzE,EAAE;AACF,oEAAoE;AACpE,wEAAwE;AACxE,kFAAkF;AAClF,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAC7E,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,4EAA4E;AAC5E,8EAA8E;AAC9E,0EAA0E;AAC1E,EAAE;AACF,4EAA4E;AAC5E,4FAA4F;AAC5F,qFAAqF;AACrF,8EAA8E;AAC9E,sFAAsF;AACtF,6CAA6C;
|
|
1
|
+
{"version":3,"file":"sliceDoc.js","sourceRoot":"","sources":["../../src/catalog/sliceDoc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CommitInfo } from "./changelog-git.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build the `em changelog` markdown document for an already-resolved commit list
|
|
4
|
+
* (oldest -> newest, see `listModelCommits`). Compiles every revision once; per-revision
|
|
5
|
+
* warnings are deliberately never surfaced (historical revisions can be noisy) — only a compile
|
|
6
|
+
* *failure* (parse error or validation error, same threshold `em diff` uses) surfaces, as that
|
|
7
|
+
* entry's error note, never a crash. Diffs are computed against the previous *parseable*
|
|
8
|
+
* revision, so a single bad revision in the middle of the walk doesn't break every entry after
|
|
9
|
+
* it. Content is read at each commit's own path (`readFileAtCommit`), so the walk survives
|
|
10
|
+
* renames.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildChangelogDoc(file: string, repoRoot: string, commits: CommitInfo[]): string;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// `em changelog`'s document builder (MIL-100), extracted out of cli.ts (MIL-167) so the MCP
|
|
3
|
+
// server's `changelog` tool can call the exact same function the CLI command does — same
|
|
4
|
+
// "one builder, two callers" pattern as buildDiffJson/buildGlossaryJson, just markdown instead
|
|
5
|
+
// of JSON (the CLI command itself has no `--json` flag; the document IS the markdown text).
|
|
6
|
+
//
|
|
7
|
+
// Already pure enough to share as-is: no process.exit, no console — a per-revision compile
|
|
8
|
+
// failure becomes that entry's `error` field, never a crash (see the docstring below). The only
|
|
9
|
+
// impure bit (`readFileAtCommit`, a `git show`) is injected in, same convention as
|
|
10
|
+
// changelog-git.ts/diff-inputs.ts's GitRunner.
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { compile } from "../pipeline.js";
|
|
14
|
+
import { ParseError } from "../parser/parser.js";
|
|
15
|
+
import { hasErrors } from "../model/validate.js";
|
|
16
|
+
import { diffModels } from "../model/diff.js";
|
|
17
|
+
import { buildChangelog, parseDecisionsLog } from "../emit/changelog.js";
|
|
18
|
+
import { readFileAtCommit } from "./changelog-git.js";
|
|
19
|
+
import { STATE_FILE_NAME } from "./stateFile.js";
|
|
20
|
+
/**
|
|
21
|
+
* Build the `em changelog` markdown document for an already-resolved commit list
|
|
22
|
+
* (oldest -> newest, see `listModelCommits`). Compiles every revision once; per-revision
|
|
23
|
+
* warnings are deliberately never surfaced (historical revisions can be noisy) — only a compile
|
|
24
|
+
* *failure* (parse error or validation error, same threshold `em diff` uses) surfaces, as that
|
|
25
|
+
* entry's error note, never a crash. Diffs are computed against the previous *parseable*
|
|
26
|
+
* revision, so a single bad revision in the middle of the walk doesn't break every entry after
|
|
27
|
+
* it. Content is read at each commit's own path (`readFileAtCommit`), so the walk survives
|
|
28
|
+
* renames.
|
|
29
|
+
*/
|
|
30
|
+
export function buildChangelogDoc(file, repoRoot, commits) {
|
|
31
|
+
const models = [];
|
|
32
|
+
const refsList = [];
|
|
33
|
+
const errors = [];
|
|
34
|
+
for (const c of commits) {
|
|
35
|
+
const rev = readFileAtCommit(repoRoot, c);
|
|
36
|
+
if (!rev.ok) {
|
|
37
|
+
models.push(null);
|
|
38
|
+
refsList.push(null);
|
|
39
|
+
errors.push(rev.message);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const { model, refs, diagnostics } = compile(rev.content);
|
|
44
|
+
if (hasErrors(diagnostics)) {
|
|
45
|
+
models.push(null);
|
|
46
|
+
refsList.push(null);
|
|
47
|
+
errors.push(`validation errors at ${c.shortHash} — fix with \`em validate\` at that revision`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
models.push(model);
|
|
51
|
+
refsList.push(refs);
|
|
52
|
+
errors.push(null);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
models.push(null);
|
|
57
|
+
refsList.push(null);
|
|
58
|
+
errors.push(e instanceof ParseError ? `parse error: ${e.message}` : e instanceof Error ? e.message : String(e));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const entries = [];
|
|
62
|
+
let prevParseable = -1;
|
|
63
|
+
commits.forEach((c, i) => {
|
|
64
|
+
const base = { shortHash: c.shortHash, date: c.date, subject: c.subject };
|
|
65
|
+
if (i === 0) {
|
|
66
|
+
entries.push({ ...base, diff: null });
|
|
67
|
+
}
|
|
68
|
+
else if (!models[i]) {
|
|
69
|
+
entries.push({ ...base, diff: null, error: errors[i] });
|
|
70
|
+
}
|
|
71
|
+
else if (prevParseable === -1) {
|
|
72
|
+
entries.push({ ...base, diff: null, error: `no earlier parseable revision to diff against (${commits[0].shortHash}: ${errors[0]})` });
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
entries.push({
|
|
76
|
+
...base,
|
|
77
|
+
diff: diffModels(models[prevParseable], models[i], refsList[prevParseable], refsList[i]),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (models[i])
|
|
81
|
+
prevParseable = i;
|
|
82
|
+
});
|
|
83
|
+
const intro = models[0] ? { slices: models[0].slices.length, elements: models[0].elements.length } : null;
|
|
84
|
+
const introError = models[0] ? undefined : (errors[0] ?? undefined);
|
|
85
|
+
const stateFile = join(dirname(file), STATE_FILE_NAME);
|
|
86
|
+
const decisions = existsSync(stateFile) ? parseDecisionsLog(readFileSync(stateFile, "utf8")) : [];
|
|
87
|
+
return buildChangelog(entries, decisions, { file, intro, introError });
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=changelogBuild.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelogBuild.js","sourceRoot":"","sources":["../../src/cli/changelogBuild.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export declare const CI_WORKFLOW_MARKER = "em-ci";
|
|
2
|
+
export declare const CONFORM_WORKFLOW_MARKER = "em-conform";
|
|
3
|
+
export declare function ciWorkflowPath(repoRoot: string): string;
|
|
4
|
+
export declare function conformWorkflowPath(repoRoot: string): string;
|
|
5
|
+
export declare function findUnsafeCiInitArg(value: string): string | null;
|
|
6
|
+
/** The managed block only (no header/`on:`/`jobs:` scaffolding) — what gets written between
|
|
7
|
+
* the marker pair, both for a fresh file and to patch an existing marked one in place. */
|
|
8
|
+
export declare function ciManagedBody(model: string, testsDir: string): string;
|
|
9
|
+
export declare function buildCiWorkflowFile(model: string, testsDir: string, emVersion: string): string;
|
|
10
|
+
/** The managed block only (no header/`on:`/`permissions:`/`jobs:` scaffolding) — what gets
|
|
11
|
+
* written between the marker pair, both for a fresh file and to patch an existing marked one
|
|
12
|
+
* in place. */
|
|
13
|
+
export declare function conformManagedBody(model: string): string;
|
|
14
|
+
export declare function buildConformWorkflowFile(model: string, emVersion: string): string;
|
|
15
|
+
export type CiFileStatus = {
|
|
16
|
+
kind: "create";
|
|
17
|
+
content: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "ok";
|
|
20
|
+
content: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "stale";
|
|
23
|
+
content: string;
|
|
24
|
+
current: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "missing-markers";
|
|
27
|
+
} | {
|
|
28
|
+
kind: "would-replace";
|
|
29
|
+
content: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Decide what to do with one generated file: create it if missing, patch the managed block if
|
|
33
|
+
* the marker pair is present, or (without `force`) leave an existing unmarked file alone. Pure
|
|
34
|
+
* — never touches disk; `applyCiFile` below does the actual write.
|
|
35
|
+
*
|
|
36
|
+
* `generated` is the full from-scratch file content (header + `on:`/`jobs:` scaffolding +
|
|
37
|
+
* marker-wrapped body) — written verbatim for `create`/`would-replace`. `managedBody` is just
|
|
38
|
+
* the text between the markers, reused to patch an existing marked file in place without
|
|
39
|
+
* touching whatever a repo added around it.
|
|
40
|
+
*/
|
|
41
|
+
export declare function planCiFile(path: string, generated: string, managedBody: string, markerName: string, force: boolean): CiFileStatus;
|
|
42
|
+
export declare function applyCiFile(path: string, status: CiFileStatus): void;
|