@milehimikey/em 1.12.0 → 1.13.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 +5 -2
- package/.claude/skills/event-modeling-conform/SKILL.md +20 -9
- package/.claude/skills/event-modeling-conform/reference/conform.md +107 -10
- package/.claude/skills/event-modeling-design/SKILL.md +26 -4
- package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
- package/.claude/skills/event-modeling-implement/SKILL.md +7 -4
- package/.claude/skills/event-modeling-implement/reference/implement.md +26 -8
- package/.claude/skills/event-modeling-review/SKILL.md +5 -1
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +45 -6
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +13 -3
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +39 -0
- package/.claude/skills/event-modeling-shared/templates/conformance-report.md +8 -1
- package/.claude/skills/event-modeling-shared/templates/state.md +3 -0
- package/README.md +1 -0
- package/dist/catalog/docJoin.d.ts +9 -0
- package/dist/catalog/docJoin.js +6 -0
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/driftSignal.d.ts +11 -5
- package/dist/catalog/driftSignal.js +18 -4
- package/dist/catalog/driftSignal.js.map +1 -1
- package/dist/catalog/modelVersionValidate.d.ts +9 -0
- package/dist/catalog/modelVersionValidate.js +38 -0
- package/dist/catalog/modelVersionValidate.js.map +1 -0
- package/dist/catalog/sliceDoc.d.ts +12 -0
- package/dist/catalog/sliceDoc.js +15 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/cli/ciInit.d.ts +24 -0
- package/dist/cli/ciInit.js +85 -15
- package/dist/cli/ciInit.js.map +1 -1
- package/dist/cli/conformScope.d.ts +1 -0
- package/dist/cli/conformScope.js +3 -1
- package/dist/cli/conformScope.js.map +1 -1
- package/dist/cli/conformSupersede.d.ts +24 -2
- package/dist/cli/conformSupersede.js +44 -3
- package/dist/cli/conformSupersede.js.map +1 -1
- package/dist/cli/coverage.d.ts +16 -2
- package/dist/cli/coverage.js +26 -4
- package/dist/cli/coverage.js.map +1 -1
- package/dist/cli/findings.d.ts +134 -0
- package/dist/cli/findings.js +355 -0
- package/dist/cli/findings.js.map +1 -0
- package/dist/cli/metrics.d.ts +93 -0
- package/dist/cli/metrics.js +331 -0
- package/dist/cli/metrics.js.map +1 -0
- package/dist/cli/modelVersion.d.ts +139 -0
- package/dist/cli/modelVersion.js +256 -0
- package/dist/cli/modelVersion.js.map +1 -0
- package/dist/cli/reratify.d.ts +24 -1
- package/dist/cli/reratify.js +73 -17
- package/dist/cli/reratify.js.map +1 -1
- package/dist/cli/skillCheck.js +2 -2
- package/dist/cli/skillCheck.js.map +1 -1
- package/dist/cli/sliceConform.d.ts +50 -0
- package/dist/cli/sliceConform.js +206 -0
- package/dist/cli/sliceConform.js.map +1 -0
- package/dist/cli/sliceIndex.d.ts +2 -0
- package/dist/cli/sliceIndex.js +15 -3
- package/dist/cli/sliceIndex.js.map +1 -1
- package/dist/cli/sliceNew.d.ts +12 -0
- package/dist/cli/sliceNew.js +23 -0
- package/dist/cli/sliceNew.js.map +1 -1
- package/dist/cli/sliceStubAll.d.ts +76 -0
- package/dist/cli/sliceStubAll.js +189 -0
- package/dist/cli/sliceStubAll.js.map +1 -0
- package/dist/cli/stateFile.d.ts +63 -7
- package/dist/cli/stateFile.js +179 -18
- package/dist/cli/stateFile.js.map +1 -1
- package/dist/cli/status.d.ts +79 -1
- package/dist/cli/status.js +134 -15
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/upgrade.d.ts +132 -0
- package/dist/cli/upgrade.js +524 -0
- package/dist/cli/upgrade.js.map +1 -0
- package/dist/cli.js +609 -50
- package/dist/cli.js.map +1 -1
- package/dist/emit/coverageJson.d.ts +5 -2
- package/dist/emit/coverageJson.js +6 -2
- package/dist/emit/coverageJson.js.map +1 -1
- package/dist/emit/freshnessJson.d.ts +1 -1
- package/dist/emit/freshnessJson.js +3 -1
- package/dist/emit/freshnessJson.js.map +1 -1
- package/dist/emit/json.d.ts +11 -1
- package/dist/emit/json.js +35 -1
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/metricsJson.d.ts +7 -0
- package/dist/emit/metricsJson.js +27 -0
- package/dist/emit/metricsJson.js.map +1 -0
- package/dist/emit/statusJson.d.ts +1 -1
- package/dist/emit/statusJson.js +17 -1
- package/dist/emit/statusJson.js.map +1 -1
- package/dist/emit/upgradeJson.d.ts +5 -0
- package/dist/emit/upgradeJson.js +29 -0
- package/dist/emit/upgradeJson.js.map +1 -0
- package/dist/emit/validateJson.d.ts +6 -2
- package/dist/emit/validateJson.js +12 -5
- package/dist/emit/validateJson.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +137 -9
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/rules.d.ts +7 -0
- package/dist/model/rules.js +12 -0
- package/dist/model/rules.js.map +1 -1
- package/dist/model/validate.d.ts +20 -0
- package/dist/model/validate.js +94 -45
- package/dist/model/validate.js.map +1 -1
- package/dist/templates.d.ts +2 -2
- package/dist/templates.js +7 -1
- package/dist/templates.js.map +1 -1
- package/dist/util/semver.d.ts +19 -0
- package/dist/util/semver.js +54 -0
- package/dist/util/semver.js.map +1 -0
- package/package.json +2 -2
|
@@ -69,6 +69,9 @@ to decide `frontmatter-invalid` without re-deriving frontmatter-shape rules of i
|
|
|
69
69
|
| `ratifiedOn` | string | `YYYY-MM-DD` | written only by `em slice ratify` (MIL-165); joined into `em export`'s `slice.doc.ratifiedOn` (schema `1.8`) |
|
|
70
70
|
| `owner` | string | free text (typically a person or team name) | hand-filled — no `em` command writes it; joined into `em export`'s `slice.doc.owner` (schema `1.9`, MIL-171), `em slice index`'s Owner column, and `em status`'s per-slice `owners[]` |
|
|
71
71
|
| `tracking` | string | free text (typically an external ticket/issue URL) | hand-filled — no `em` command writes it; joined into `em export`'s `slice.doc.tracking` (schema `1.9`, MIL-171) and `em slice index`'s Tracking column. This is the exact field `em-tracker-bridge` reads to find the ticket mirroring this slice — `em` only stores and displays it, it never talks to a tracker itself |
|
|
72
|
+
| `conformedVersion` | integer | positive integer | written only by `em slice conform` (MIL-214 — see [cli.md](https://github.com/milehimikey/em/blob/main/docs/cli.md#em-slice-conform-file-slice-key---at-rev)): this slice's `version:` at the time a conform sweep last certified it. Joined into `em export`'s `slice.doc.conformedVersion` (schema `1.13`); paired with `version` to compute `slice.doc.driftSignal`'s `"uncertified"` case |
|
|
73
|
+
| `conformedAt` | string | free text (a target-repo revision, NOT a `<slice-key>@v<N>` lineage ref) | written only by `em slice conform` (MIL-214): the revision that certification sweep diffed against. Joined into `em export`'s `slice.doc.conformedAt` (schema `1.13`) and `em slice index`'s Conformed column |
|
|
74
|
+
| `conformedOn` | string | `YYYY-MM-DD` | written only by `em slice conform` (MIL-214). Joined into `em export`'s `slice.doc.conformedOn` (schema `1.13`) |
|
|
72
75
|
|
|
73
76
|
Five keys — `schemaVersion`/`pattern`/`swimlane`/`status`/`version` — are what
|
|
74
77
|
`hasUsableFrontmatter()` requires: a doc omitting any of them is `frontmatter-invalid` to
|
|
@@ -89,6 +92,7 @@ convention, not parser-enforced.
|
|
|
89
92
|
| `reviewedBy`, `reviewedOn` | optional in every state — present once `em slice review` (MIL-201) has run at least once; a doc that never went through the review gate, or was hand-flipped to `reviewed`, simply omits both. `em slice ratify` never clears them; `em slice reratify` does (they describe the version that shipped) |
|
|
90
93
|
| `ratifiedBy`, `ratifiedOn` | optional in every state — present once `em slice ratify` (MIL-165) has run at least once; a doc predating this feature, or ratified by hand, simply omits both |
|
|
91
94
|
| `owner`, `tracking` | optional in every state — hand-filled whenever a team wants a who-holds-this / external-tracker link; most docs never carry either (MIL-171) |
|
|
95
|
+
| `conformedVersion`, `conformedAt`, `conformedOn` | optional in every state — present once `em slice conform` (MIL-214) has run at least once for the CURRENT version; a doc that's never been through a conform sweep, or whose `version` has since bumped past what was certified, simply has none of the three (or a stale triple — see `driftSignal`'s `uncertified` case below) |
|
|
92
96
|
|
|
93
97
|
`em export`'s slice-doc join (MIL-91) is the first `em` command to mechanically check the
|
|
94
98
|
required row above: a bound doc missing any of `schemaVersion`/`pattern`/`swimlane`/`status`/
|
|
@@ -101,6 +105,32 @@ No `em` command fails a build over this table. `em validate`'s frontmatter-coher
|
|
|
101
105
|
(never fails) on the one combination checkable without git history: `status: implemented` with
|
|
102
106
|
no `implementedIn` link at all.
|
|
103
107
|
|
|
108
|
+
## Stub docs
|
|
109
|
+
|
|
110
|
+
`em slice new --stub` and `em slice stub-all` (MIL-184, ruled 2026-09-08 on GH #128 — see that
|
|
111
|
+
issue for the fuller design rationale) write a **stub**: the exact same 5 required-at-`status:
|
|
112
|
+
draft` frontmatter keys above, but a one-line placeholder body (`_Stub — deepen with the slice
|
|
113
|
+
phase (see slice-doc-schema.md)._`) instead of the diagram-image stub and every judgment section
|
|
114
|
+
(Intent, Command, Scenarios, Open Questions, …) a full slice doc eventually carries.
|
|
115
|
+
|
|
116
|
+
A stub is not a lesser doc, frontmatter-wise — it's a real doc with a real `status`, parsed and
|
|
117
|
+
joined by `em export`/`em catalog`/`em status`/`em slice index` exactly like a fully-written one,
|
|
118
|
+
because it's the same frontmatter dialect. The doc stays the single source of truth for
|
|
119
|
+
`status`; a stub is just the cheapest possible way to give a slice one before anyone's ready to
|
|
120
|
+
write the real spec.
|
|
121
|
+
|
|
122
|
+
**A stub CAN pass `em validate --slice-ready`.** That gate checks four things (`computeSliceReadyGates`,
|
|
123
|
+
[validation.md#slice-readiness](https://github.com/milehimikey/em/blob/main/docs/validation.md#slice-readiness)): a doc is bound, its frontmatter
|
|
124
|
+
is usable, `status` is `ready-to-implement`, and its Open Questions count has none unchecked. A
|
|
125
|
+
stub with no `## Open Questions` section at all has `openQuestionsTotal: 0` (see
|
|
126
|
+
[Open Questions section: lifecycle](#open-questions-section-lifecycle) below) — zero unchecked is
|
|
127
|
+
vacuously true — so a stub escalated to `ready-to-implement` (`em slice stub-all --status
|
|
128
|
+
ready-to-implement --by <name>`) passes cleanly. The readiness gate is frontmatter-plus-checkbox-
|
|
129
|
+
count only; it has no opinion about whether Intent/Command/Scenarios/Invariants were ever
|
|
130
|
+
written. Demanding that content is the `slice` phase's (and, for a live walkthrough, the review
|
|
131
|
+
skill's) job, not a machine gate's — see `../.claude/skills/event-modeling-design/SKILL.md` and
|
|
132
|
+
`../.claude/skills/event-modeling-review/SKILL.md`.
|
|
133
|
+
|
|
104
134
|
## Lineage: grammar and cardinality
|
|
105
135
|
|
|
106
136
|
Grammar: `<slice-key>@v<N>` — `<slice-key>` is the referenced slice's kebab-case filename stem
|
|
@@ -368,6 +398,15 @@ frontmatter-coherence check (MIL-85) deliberately never flags this combination
|
|
|
368
398
|
`status: implemented` with no `implementedIn` link at all is checkable incoherence; see
|
|
369
399
|
[validation.md#frontmatter-coherence](https://github.com/milehimikey/em/blob/main/docs/validation.md#frontmatter-coherence).
|
|
370
400
|
|
|
401
|
+
**An unshipped version has nothing to flip back** (MIL-258). A ratified doc that has not shipped
|
|
402
|
+
yet (`status: ready-to-implement` with `ratifiedBy:` set) changes the same way: `em slice
|
|
403
|
+
reratify` bumps `version:` and clears `ratifiedBy`/`ratifiedOn`/`reviewedBy`/`reviewedOn`, but
|
|
404
|
+
leaves `status` at `ready-to-implement` (and any `implementedIn` as it was — an absent one stays
|
|
405
|
+
absent). This is the path for an answered gap mid-build. In both cases the new version is not
|
|
406
|
+
ratified until `em slice ratify --by <name>` records the sign-off. A `ready-to-implement` doc with
|
|
407
|
+
no `ratifiedBy` is awaiting that sign-off, so a second `reratify` refuses rather than
|
|
408
|
+
double-bump; `draft`/`reviewed` docs are not ratified and are simply edited.
|
|
409
|
+
|
|
371
410
|
Pair a re-ratification with a `## Delta` section (see
|
|
372
411
|
[Delta section: grammar and lifecycle](#delta-section-grammar-and-lifecycle) above) recording
|
|
373
412
|
the ratified change in typed operation blocks, so it's reviewable without opening git.
|
|
@@ -31,7 +31,14 @@ Replace bracketed placeholders; delete guidance comments before finishing.
|
|
|
31
31
|
## Findings
|
|
32
32
|
|
|
33
33
|
<!-- One subsection per finding. Group however reads clearest (by slice, or by surface) but keep
|
|
34
|
-
every field below for every finding. Order doesn't matter; classification does.
|
|
34
|
+
every field below for every finding. Order doesn't matter; classification does.
|
|
35
|
+
|
|
36
|
+
MIL-214: this section's structured twin is `conformance/{{YYYY-MM-DD}}-findings.json`,
|
|
37
|
+
written ALONGSIDE this report (not after it) — one JSON entry per `### n.` finding below,
|
|
38
|
+
`id` matching `{{n}}`. See reference/conform.md step 5 for the exact shape and the write-then-
|
|
39
|
+
`em conform-findings check`-it discipline. A later `em conform-supersede --locus --by` call
|
|
40
|
+
records the ruling on both the JSON (locus/resolvedBy/resolvedOn) and this report's banner
|
|
41
|
+
in one call. -->
|
|
35
42
|
|
|
36
43
|
### {{n}}. {{Short finding title}}
|
|
37
44
|
|
|
@@ -7,10 +7,13 @@ Keep it current at the end of every working session.
|
|
|
7
7
|
# Event Modeling Progress — {{Model Name}}
|
|
8
8
|
|
|
9
9
|
- **Model file:** `{{model-name}}.em`
|
|
10
|
+
- **Em version:** {{X.Y.Z}}
|
|
10
11
|
- **Current phase:** {{discover | extract | model | slice | implement | conform | review | validate}}
|
|
11
12
|
- **Current step:** {{1–7, see methodology; or extraction round R1–R7}}
|
|
12
13
|
- **Last updated:** {{YYYY-MM-DD}}
|
|
13
14
|
- **Last conformance:** {{YYYY-MM-DD @ <target-repo revision> — report: conformance/<date>-report.md | never}}
|
|
15
|
+
- **Model version:** {{N | none}}
|
|
16
|
+
- **Certified:** {{v<N> @ <target-repo revision> (YYYY-MM-DD) | never}}
|
|
14
17
|
- **Last stakeholder review:** {{YYYY-MM-DD — attendees: see Participants | never}}
|
|
15
18
|
|
|
16
19
|
## Session inputs
|
package/README.md
CHANGED
|
@@ -118,6 +118,7 @@ built this way.
|
|
|
118
118
|
| [docs/cli.md](docs/cli.md) | Every command and flag |
|
|
119
119
|
| [docs/validation.md](docs/validation.md) | Every rule `em validate` checks, and the fixes |
|
|
120
120
|
| [docs/ci.md](docs/ci.md) | Copy-paste CI recipes: validate `.em` changes, run conformance on a schedule |
|
|
121
|
+
| [docs/upgrading.md](docs/upgrading.md) | Bring a model repo forward across em releases: `em upgrade`, release by release |
|
|
121
122
|
| [docs/timeline.md](docs/timeline.md) | The Two Laws of the Timeline |
|
|
122
123
|
| [docs/ai-workflow.md](docs/ai-workflow.md) | The Claude Code skill: install, phases, artifacts |
|
|
123
124
|
| [docs/dependencies.md](docs/dependencies.md) | What's bundled vs. what needs a system install |
|
|
@@ -73,6 +73,15 @@ export interface SliceDocExport {
|
|
|
73
73
|
* em-tracker-bridge reads to find the mirrored ticket — its name/shape here is a cross-tool
|
|
74
74
|
* contract, not just an internal display field. */
|
|
75
75
|
tracking: string | null;
|
|
76
|
+
/** MIL-214: this slice's `version:` at the time a conform sweep last certified it (frontmatter
|
|
77
|
+
* `conformedVersion:`), written only by `em slice conform` — null when never certified. */
|
|
78
|
+
conformedVersion: number | null;
|
|
79
|
+
/** MIL-214: the target repo's revision that certification sweep diffed against (frontmatter
|
|
80
|
+
* `conformedAt:`), written only by `em slice conform` — null when never certified. */
|
|
81
|
+
conformedAt: string | null;
|
|
82
|
+
/** MIL-214: the local date that certification was recorded (frontmatter `conformedOn:`),
|
|
83
|
+
* written only by `em slice conform` — null when never certified. */
|
|
84
|
+
conformedOn: string | null;
|
|
76
85
|
}
|
|
77
86
|
export interface SliceDocJoinResult {
|
|
78
87
|
doc: SliceDocExport;
|
package/dist/catalog/docJoin.js
CHANGED
|
@@ -73,6 +73,9 @@ const EMPTY_CONTENT = {
|
|
|
73
73
|
ratifiedOn: null,
|
|
74
74
|
owner: null,
|
|
75
75
|
tracking: null,
|
|
76
|
+
conformedVersion: null,
|
|
77
|
+
conformedAt: null,
|
|
78
|
+
conformedOn: null,
|
|
76
79
|
};
|
|
77
80
|
/**
|
|
78
81
|
* Resolve one slice's doc join. `baseDir` is the `.em` file's directory (doc paths, like note
|
|
@@ -184,6 +187,9 @@ function foundDoc(path, parsed) {
|
|
|
184
187
|
ratifiedOn: parsed.ratifiedOn,
|
|
185
188
|
owner: parsed.owner,
|
|
186
189
|
tracking: parsed.tracking,
|
|
190
|
+
conformedVersion: parsed.conformedVersion,
|
|
191
|
+
conformedAt: parsed.conformedAt,
|
|
192
|
+
conformedOn: parsed.conformedOn,
|
|
187
193
|
};
|
|
188
194
|
}
|
|
189
195
|
//# 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,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,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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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SliceDoc } from "./sliceDoc.js";
|
|
2
2
|
export type DriftSignalKind =
|
|
3
|
-
/** status: implemented, implementedIn set —
|
|
3
|
+
/** status: implemented, implementedIn set, conformedVersion === version — certified current. */
|
|
4
4
|
"in-sync"
|
|
5
5
|
/** status not implemented, implementedIn absent — normal pre-ship state. */
|
|
6
6
|
| "never-implemented"
|
|
@@ -8,9 +8,15 @@ export type DriftSignalKind =
|
|
|
8
8
|
* Expected, not a defect: never surface this as fresh drift or a validate diagnostic. */
|
|
9
9
|
| "unpropagated-delta"
|
|
10
10
|
/** status: implemented, implementedIn absent — genuine incoherence (em validate warns). */
|
|
11
|
-
| "implemented-without-link"
|
|
11
|
+
| "implemented-without-link"
|
|
12
|
+
/** status: implemented, implementedIn set, but conformedVersion is absent or doesn't match the
|
|
13
|
+
* current version — nobody has certified THIS version yet. Expected post-ship default, not a
|
|
14
|
+
* defect: never surface this as an `em validate` diagnostic (see
|
|
15
|
+
* catalog/frontmatterCoherenceValidate.ts, which only ever flags implemented-without-link). */
|
|
16
|
+
| "uncertified";
|
|
12
17
|
/**
|
|
13
|
-
* Classify a doc's status/implementedIn
|
|
14
|
-
* callers building a partial/synthetic doc (tests, future
|
|
18
|
+
* Classify a doc's status/implementedIn/version/conformedVersion quadruple. Takes a `Pick`
|
|
19
|
+
* rather than the full `SliceDoc` so callers building a partial/synthetic doc (tests, future
|
|
20
|
+
* callers) don't need every field.
|
|
15
21
|
*/
|
|
16
|
-
export declare function classifyImplementationDrift(doc: Pick<SliceDoc, "status" | "implementedIn">): DriftSignalKind;
|
|
22
|
+
export declare function classifyImplementationDrift(doc: Pick<SliceDoc, "status" | "implementedIn" | "version" | "conformedVersion">): DriftSignalKind;
|
|
@@ -11,14 +11,28 @@
|
|
|
11
11
|
// naming the prior version's PR. That combination — `unpropagated-delta` — is the EXPECTED drift
|
|
12
12
|
// signal, not staleness, and must never be flagged as incoherence. Only `implemented-without-link`
|
|
13
13
|
// (status: implemented, no link at all) is a genuine coherence problem worth a diagnostic.
|
|
14
|
+
//
|
|
15
|
+
// MIL-214: certification is per slice PER VERSION, not per model — `implementedIn` only ever
|
|
16
|
+
// said "code exists somewhere for this slice," never "the code that's there was actually walked
|
|
17
|
+
// against version N of this doc." `conformedVersion` (frontmatter, written only by
|
|
18
|
+
// `em slice conform` — cli/sliceConform.ts) records the version a conform run last certified.
|
|
19
|
+
// `uncertified` is the new, EXPECTED post-ship default: a slice reaches `status: implemented`
|
|
20
|
+
// long before anyone runs a conform sweep against it, and every version bump (`em slice
|
|
21
|
+
// reratify`) invalidates the prior certification even though `status`/`implementedIn` still read
|
|
22
|
+
// "shipped" — so this is not folded into `in-sync` and not flagged by `em validate` (same
|
|
23
|
+
// "expected, not a defect" treatment `unpropagated-delta` gets).
|
|
14
24
|
/**
|
|
15
|
-
* Classify a doc's status/implementedIn
|
|
16
|
-
* callers building a partial/synthetic doc (tests, future
|
|
25
|
+
* Classify a doc's status/implementedIn/version/conformedVersion quadruple. Takes a `Pick`
|
|
26
|
+
* rather than the full `SliceDoc` so callers building a partial/synthetic doc (tests, future
|
|
27
|
+
* callers) don't need every field.
|
|
17
28
|
*/
|
|
18
29
|
export function classifyImplementationDrift(doc) {
|
|
19
30
|
const hasLink = typeof doc.implementedIn === "string" && doc.implementedIn.trim().length > 0;
|
|
20
|
-
if (doc.status === "implemented")
|
|
21
|
-
|
|
31
|
+
if (doc.status === "implemented") {
|
|
32
|
+
if (!hasLink)
|
|
33
|
+
return "implemented-without-link";
|
|
34
|
+
return doc.conformedVersion !== null && doc.conformedVersion === doc.version ? "in-sync" : "uncertified";
|
|
35
|
+
}
|
|
22
36
|
return hasLink ? "unpropagated-delta" : "never-implemented";
|
|
23
37
|
}
|
|
24
38
|
//# sourceMappingURL=driftSignal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driftSignal.js","sourceRoot":"","sources":["../../src/catalog/driftSignal.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,4FAA4F;AAC5F,uFAAuF;AACvF,2FAA2F;AAC3F,iGAAiG;AACjG,yFAAyF;AACzF,kCAAkC;AAClC,EAAE;AACF,6FAA6F;AAC7F,iGAAiG;AACjG,iGAAiG;AACjG,mGAAmG;AACnG,2FAA2F;
|
|
1
|
+
{"version":3,"file":"driftSignal.js","sourceRoot":"","sources":["../../src/catalog/driftSignal.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,4FAA4F;AAC5F,uFAAuF;AACvF,2FAA2F;AAC3F,iGAAiG;AACjG,yFAAyF;AACzF,kCAAkC;AAClC,EAAE;AACF,6FAA6F;AAC7F,iGAAiG;AACjG,iGAAiG;AACjG,mGAAmG;AACnG,2FAA2F;AAC3F,EAAE;AACF,6FAA6F;AAC7F,gGAAgG;AAChG,mFAAmF;AACnF,8FAA8F;AAC9F,8FAA8F;AAC9F,wFAAwF;AACxF,iGAAiG;AACjG,0FAA0F;AAC1F,iEAAiE;AAoBjE;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CACzC,GAAgF;IAEhF,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7F,IAAI,GAAG,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACjC,IAAI,CAAC,OAAO;YAAE,OAAO,0BAA0B,CAAC;QAChD,OAAO,GAAG,CAAC,gBAAgB,KAAK,IAAI,IAAI,GAAG,CAAC,gBAAgB,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC;IAC3G,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,mBAAmB,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NormalizedModel } from "../model/model.js";
|
|
2
|
+
import { RefsResult } from "../model/refs.js";
|
|
3
|
+
import { Diagnostic } from "../model/validate.js";
|
|
4
|
+
/**
|
|
5
|
+
* `baseDir` is the `.em` file's directory (manifests live at `<baseDir>/model-versions/`, same
|
|
6
|
+
* convention every other doc/note/manifest path in `em` uses); `source` is the `.em` file's own
|
|
7
|
+
* text, for the modelHash comparison.
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateModelVersionStale(model: NormalizedModel, refs: RefsResult, baseDir: string, source: string): Diagnostic[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// `em validate`'s `model-version-stale` rule (MIL-218): warns when the `.em` content hash or
|
|
3
|
+
// the slice-version vector has moved since the last `em model version bump` — the same
|
|
4
|
+
// `modelVersionDrift` predicate `em status` and the `em slice ratify`/`reratify` advisory
|
|
5
|
+
// share, never re-derived a third way. Same fs-aware-sibling shape as
|
|
6
|
+
// catalog/frontmatterCoherenceValidate.ts/orphanedSliceDocValidate.ts: a module next to
|
|
7
|
+
// model/validate.ts, not folded into it, because it needs `baseDir`/fs access (the
|
|
8
|
+
// `model-versions/*.json` manifest) the rest of validate deliberately never touches.
|
|
9
|
+
//
|
|
10
|
+
// Silent when no manifest exists at all (`drift.current === null`) — a repo that never bumped a
|
|
11
|
+
// design version has nothing to be stale against; nagging a project that has never opted into
|
|
12
|
+
// this feature would be exactly the "surprise new warning on every model" failure mode the
|
|
13
|
+
// ruling's "silent when no manifest exists" clause exists to avoid. Warning, never error:
|
|
14
|
+
// nothing about a stale design version blocks anything else in the model.
|
|
15
|
+
import { pushDiag } from "../model/rules.js";
|
|
16
|
+
import { modelVersionDrift } from "../cli/modelVersion.js";
|
|
17
|
+
/**
|
|
18
|
+
* `baseDir` is the `.em` file's directory (manifests live at `<baseDir>/model-versions/`, same
|
|
19
|
+
* convention every other doc/note/manifest path in `em` uses); `source` is the `.em` file's own
|
|
20
|
+
* text, for the modelHash comparison.
|
|
21
|
+
*/
|
|
22
|
+
export function validateModelVersionStale(model, refs, baseDir, source) {
|
|
23
|
+
const drift = modelVersionDrift(baseDir, model, refs, source);
|
|
24
|
+
if (drift.current === null || (!drift.hashChanged && drift.slicesChanged.length === 0))
|
|
25
|
+
return [];
|
|
26
|
+
const parts = [];
|
|
27
|
+
if (drift.hashChanged)
|
|
28
|
+
parts.push("the model's own content changed");
|
|
29
|
+
if (drift.slicesChanged.length > 0) {
|
|
30
|
+
parts.push(`${drift.slicesChanged.length} slice(s) moved (${drift.slicesChanged.map((c) => c.key).join(", ")})`);
|
|
31
|
+
}
|
|
32
|
+
const diags = [];
|
|
33
|
+
pushDiag(diags, "model-version-stale", {
|
|
34
|
+
message: `model version v${drift.current} is stale — ${parts.join(" and ")} since it was bumped; run \`em model version bump\` to record it`,
|
|
35
|
+
});
|
|
36
|
+
return diags;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=modelVersionValidate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelVersionValidate.js","sourceRoot":"","sources":["../../src/catalog/modelVersionValidate.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,6FAA6F;AAC7F,uFAAuF;AACvF,0FAA0F;AAC1F,sEAAsE;AACtE,wFAAwF;AACxF,mFAAmF;AACnF,qFAAqF;AACrF,EAAE;AACF,gGAAgG;AAChG,8FAA8F;AAC9F,2FAA2F;AAC3F,0FAA0F;AAC1F,0EAA0E;AAK1E,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAsB,EAAE,IAAgB,EAAE,OAAe,EAAE,MAAc;IACjH,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9D,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAElG,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IACrE,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,aAAa,CAAC,MAAM,oBAAoB,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnH,CAAC;IACD,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,qBAAqB,EAAE;QACrC,OAAO,EAAE,kBAAkB,KAAK,CAAC,OAAO,eAAe,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,kEAAkE;KAC7I,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -82,6 +82,18 @@ export interface SliceDoc {
|
|
|
82
82
|
* to find the ticket mirroring this slice — its export name/shape (docJoin.ts) is a
|
|
83
83
|
* cross-tool contract, not just an internal display field. */
|
|
84
84
|
tracking: string | null;
|
|
85
|
+
/** MIL-214: `conformedVersion:` — this slice's `version:` at the time a conform sweep last
|
|
86
|
+
* certified it, or null when never certified. Written only by `em slice conform`. Paired with
|
|
87
|
+
* `version` to compute `driftSignal`'s `uncertified` case (catalog/driftSignal.ts). */
|
|
88
|
+
conformedVersion: number | null;
|
|
89
|
+
/** MIL-214: `conformedAt:` — the target repo's revision that certification sweep diffed
|
|
90
|
+
* against, or null when absent. Written only by `em slice conform`; a plain revision string,
|
|
91
|
+
* NOT a `<slice-key>@v<N>` lineage ref. */
|
|
92
|
+
conformedAt: string | null;
|
|
93
|
+
/** MIL-214: `conformedOn:` — a `YYYY-MM-DD` date string, or null when absent. Written only by
|
|
94
|
+
* `em slice conform`; validated by the CLI layer, not re-validated here (this parser stays as
|
|
95
|
+
* lenient about value shape as every other frontmatter field). */
|
|
96
|
+
conformedOn: string | null;
|
|
85
97
|
/** True when a well-formed leading `---`/`---` frontmatter fence was found and
|
|
86
98
|
* closed — independent of which keys it contained. False for a legacy
|
|
87
99
|
* status-bullet-only doc, a doc with no frontmatter at all, or an
|
package/dist/catalog/sliceDoc.js
CHANGED
|
@@ -47,6 +47,18 @@
|
|
|
47
47
|
// particular is the field em-tracker-bridge reads to find the mirrored ticket, so its export
|
|
48
48
|
// name/shape is a cross-tool contract — see docJoin.ts/emit/json.ts. Additive,
|
|
49
49
|
// tolerate-unknown-fields: absent on every doc predating this feature.
|
|
50
|
+
//
|
|
51
|
+
// `conformedVersion`/`conformedAt`/`conformedOn` (MIL-214) are a ninth, tenth, and eleventh
|
|
52
|
+
// optional frontmatter-only field: per-slice-per-version certification, written only by
|
|
53
|
+
// `em slice conform` (cli/sliceConform.ts) — same "one write path" discipline `implementedIn`
|
|
54
|
+
// has with `em slice mark-implemented`. `conformedVersion` is this slice's own `version:` at the
|
|
55
|
+
// time a conform sweep last certified it (a cache of git truth, not derived/validated against
|
|
56
|
+
// git history — same convention `version` itself already uses); `conformedAt` is the TARGET
|
|
57
|
+
// REPO'S revision that sweep diffed against (a plain revision string, not a slice-doc lineage
|
|
58
|
+
// ref — deliberately NOT `<slice-key>@v<N>` shaped); `conformedOn` is the local date the
|
|
59
|
+
// certification was recorded. All three additive, tolerate-unknown-fields: absent on every doc
|
|
60
|
+
// predating this feature, or one never yet conformed. See catalog/driftSignal.ts's `uncertified`
|
|
61
|
+
// classification, which compares `conformedVersion` against `version`.
|
|
50
62
|
import { marked } from "marked";
|
|
51
63
|
/** Frontmatter keys docs/slice-doc-schema.md's required-vs-optional table marks
|
|
52
64
|
* required at every `status` — the mechanical basis for `frontmatter-invalid`
|
|
@@ -202,6 +214,9 @@ export function parseSliceDoc(markdown) {
|
|
|
202
214
|
ratifiedOn: fields.get("ratifiedon") ?? null,
|
|
203
215
|
owner: fields.get("owner") ?? null,
|
|
204
216
|
tracking: fields.get("tracking") ?? null,
|
|
217
|
+
conformedVersion: parseVersion(fields.get("conformedversion")),
|
|
218
|
+
conformedAt: fields.get("conformedat") ?? null,
|
|
219
|
+
conformedOn: fields.get("conformedon") ?? null,
|
|
205
220
|
frontmatterPresent,
|
|
206
221
|
missingRequiredFields: REQUIRED_FRONTMATTER_KEYS.filter((k) => !fields.has(k)),
|
|
207
222
|
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;AAC7C,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,8FAA8F;AAC9F,iGAAiG;AACjG,qFAAqF;AACrF,EAAE;AACF,6FAA6F;AAC7F,+FAA+F;AAC/F,kGAAkG;AAClG,wFAAwF;AACxF,6FAA6F;AAC7F,+EAA+E;AAC/E,uEAAuE;AAEvE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"sliceDoc.js","sourceRoot":"","sources":["../../src/catalog/sliceDoc.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/ciInit.d.ts
CHANGED
|
@@ -34,7 +34,25 @@ export type CiFileStatus = {
|
|
|
34
34
|
} | {
|
|
35
35
|
kind: "would-replace";
|
|
36
36
|
content: string;
|
|
37
|
+
} | {
|
|
38
|
+
kind: "other-model";
|
|
39
|
+
previous: string;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "replace-model";
|
|
42
|
+
content: string;
|
|
43
|
+
previous: string;
|
|
37
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Which model an existing managed block was generated for, read from the block's own content
|
|
47
|
+
* (MIL-256). Deliberately content-derived rather than a new marker line: blocks written by em
|
|
48
|
+
* 1.13.0 and earlier carry no machine-readable model, but every one of them bakes the model into
|
|
49
|
+
* the per-model `run:` lines (em-ci.yml: `slice index "<model>" --check`; em-conform.yml:
|
|
50
|
+
* `MODEL_DIR: <dir>`). Neither pattern includes the `npx @milehimikey/em@<ver>` pin, so a
|
|
51
|
+
* version difference can never read as a different model. Returns null when the block was
|
|
52
|
+
* hand-edited past recognition - callers then treat it as the same model (the conservative,
|
|
53
|
+
* pre-MIL-256 behavior) rather than refusing on a guess.
|
|
54
|
+
*/
|
|
55
|
+
export declare function managedBlockModel(markerName: string, blockBody: string): string | null;
|
|
38
56
|
/**
|
|
39
57
|
* Decide what to do with one generated file: create it if missing, patch the managed block if
|
|
40
58
|
* the marker pair is present, or (without `force`) leave an existing unmarked file alone. Pure
|
|
@@ -44,6 +62,12 @@ export type CiFileStatus = {
|
|
|
44
62
|
* marker-wrapped body) — written verbatim for `create`/`would-replace`. `managedBody` is just
|
|
45
63
|
* the text between the markers, reused to patch an existing marked file in place without
|
|
46
64
|
* touching whatever a repo added around it.
|
|
65
|
+
*
|
|
66
|
+
* MIL-256 (#174): the managed block is single-model (multi-model is MIL-233). A marked file
|
|
67
|
+
* whose block was generated for a DIFFERENT model than `managedBody` is `other-model` (refused)
|
|
68
|
+
* unless `force`, then `replace-model`. The same model with any other difference (version pin,
|
|
69
|
+
* old text) stays `stale`, exactly as before. `em upgrade`'s ci-block step re-derives the model
|
|
70
|
+
* from the existing file itself, so it only ever plans the same model.
|
|
47
71
|
*/
|
|
48
72
|
export declare function planCiFile(path: string, generated: string, managedBody: string, markerName: string, force: boolean): CiFileStatus;
|
|
49
73
|
export declare function applyCiFile(path: string, status: CiFileStatus): void;
|
package/dist/cli/ciInit.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// check` as its own gate instead of silently overwriting").
|
|
26
26
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
27
27
|
import { dirname, join } from "node:path";
|
|
28
|
-
import { applyMarker, markerPair } from "../util/markers.js";
|
|
28
|
+
import { applyMarker, markerPair, markerRegex } from "../util/markers.js";
|
|
29
29
|
export const CI_WORKFLOW_MARKER = "em-ci";
|
|
30
30
|
export const CONFORM_WORKFLOW_MARKER = "em-conform";
|
|
31
31
|
const CI_WORKFLOW_RELPATH = join(".github", "workflows", "em-ci.yml");
|
|
@@ -107,7 +107,7 @@ export function ciManagedBody(model, testsDir, emVersion) {
|
|
|
107
107
|
with:
|
|
108
108
|
node-version: 20
|
|
109
109
|
- name: Check invariant test coverage
|
|
110
|
-
# Counts INV-* IDs in \`implemented\` slice docs only (MIL-207)
|
|
110
|
+
# Counts INV-* IDs in \`implemented\` slice docs only (MIL-207) - a ready-to-implement
|
|
111
111
|
# doc has nothing yet to cite it, so this job is green on a fresh scaffold and on a
|
|
112
112
|
# doc-only ratification PR. Pass --include-ready for the older, forward-looking report.
|
|
113
113
|
run: ${em} coverage "${model}" --tests "${testsDir}" --strict
|
|
@@ -140,9 +140,26 @@ export function ciManagedBody(model, testsDir, emVersion) {
|
|
|
140
140
|
if [ -d .claude/skills/event-modeling ]; then
|
|
141
141
|
${em} skill check
|
|
142
142
|
else
|
|
143
|
-
echo "no vendored skill at .claude/skills/event-modeling
|
|
143
|
+
echo "no vendored skill at .claude/skills/event-modeling - skipping (run em skill install to opt in)"
|
|
144
144
|
fi
|
|
145
145
|
|
|
146
|
+
upgrade-check:
|
|
147
|
+
name: "em upgrade --check (advisory - hard incompatibilities only, MIL-219)"
|
|
148
|
+
if: github.event_name == 'pull_request'
|
|
149
|
+
runs-on: ubuntu-latest
|
|
150
|
+
steps:
|
|
151
|
+
- uses: actions/checkout@v4
|
|
152
|
+
with:
|
|
153
|
+
fetch-depth: 0
|
|
154
|
+
- uses: actions/setup-node@v4
|
|
155
|
+
with:
|
|
156
|
+
node-version: 20
|
|
157
|
+
- name: Check for hard upgrade incompatibilities
|
|
158
|
+
# Advisory: exits non-zero ONLY on a hard incompatibility (an unparseable state file, an
|
|
159
|
+
# old .em shape em migrate itself refuses) - never on the ordinary "some mechanical
|
|
160
|
+
# steps are applicable" case. See docs/upgrading.md.
|
|
161
|
+
run: ${em} upgrade "${model}" --check
|
|
162
|
+
|
|
146
163
|
glossary:
|
|
147
164
|
name: "em glossary --fail-on-conflicts (cross-model vocabulary)"
|
|
148
165
|
if: github.event_name == 'pull_request'
|
|
@@ -153,10 +170,12 @@ export function ciManagedBody(model, testsDir, emVersion) {
|
|
|
153
170
|
with:
|
|
154
171
|
node-version: 20
|
|
155
172
|
- name: Check glossary consistency across models
|
|
156
|
-
run:
|
|
173
|
+
run: |
|
|
174
|
+
mapfile -t models < <(git ls-files '*.em')
|
|
175
|
+
${em} glossary "\${models[@]}" --fail-on-conflicts
|
|
157
176
|
|
|
158
177
|
status-badge:
|
|
159
|
-
name: rebuild status badge (advisory
|
|
178
|
+
name: rebuild status badge (advisory - publish only, never a gate)
|
|
160
179
|
if: github.event_name == 'push'
|
|
161
180
|
runs-on: ubuntu-latest
|
|
162
181
|
permissions:
|
|
@@ -181,14 +200,14 @@ export function ciManagedBody(model, testsDir, emVersion) {
|
|
|
181
200
|
git push`;
|
|
182
201
|
}
|
|
183
202
|
function ciWorkflowHeader(model, emVersion) {
|
|
184
|
-
return `# Generated once by \`em ci init ${model}\` (em ${emVersion})
|
|
203
|
+
return `# Generated once by \`em ci init ${model}\` (em ${emVersion}) - docs/ci.md
|
|
185
204
|
#
|
|
186
205
|
# This file is yours from here: edit it, add jobs, remove ones you don't want. The content
|
|
187
206
|
# between the GENERATED:${CI_WORKFLOW_MARKER} markers below is what a future \`em ci init\`
|
|
188
|
-
# (e.g. after upgrading em) refreshes in place
|
|
207
|
+
# (e.g. after upgrading em) refreshes in place - add your own jobs above or below the markers,
|
|
189
208
|
# at the same indent under \`jobs:\`, and they survive a re-run untouched. Every gate here fails
|
|
190
209
|
# the PR the same way any other required check does; the status-badge job only ever publishes.
|
|
191
|
-
# \`em ci init ${model} --check\` reports drift in the managed block
|
|
210
|
+
# \`em ci init ${model} --check\` reports drift in the managed block - advisory unless you wire
|
|
192
211
|
# it into a gate yourself (docs/ci.md).
|
|
193
212
|
name: em ci
|
|
194
213
|
|
|
@@ -229,7 +248,7 @@ export function conformManagedBody(model, emVersion) {
|
|
|
229
248
|
node-version: 20
|
|
230
249
|
|
|
231
250
|
- name: Note reports already present
|
|
232
|
-
run:
|
|
251
|
+
run: find "$MODEL_DIR/conformance" -maxdepth 1 -name '*-report.md' 2>/dev/null | sort > /tmp/reports-before
|
|
233
252
|
|
|
234
253
|
- name: Run conform phase
|
|
235
254
|
run: |
|
|
@@ -242,10 +261,10 @@ export function conformManagedBody(model, emVersion) {
|
|
|
242
261
|
|
|
243
262
|
- name: Post report
|
|
244
263
|
run: |
|
|
245
|
-
|
|
264
|
+
find "$MODEL_DIR/conformance" -maxdepth 1 -name '*-report.md' 2>/dev/null | sort > /tmp/reports-after
|
|
246
265
|
report=$(comm -13 /tmp/reports-before /tmp/reports-after | tail -1)
|
|
247
266
|
if [ -z "$report" ]; then
|
|
248
|
-
echo "the run produced no new report
|
|
267
|
+
echo "the run produced no new report - nothing to post"
|
|
249
268
|
exit 0
|
|
250
269
|
fi
|
|
251
270
|
gh issue create --title "Model conformance report $(date +%F)" \\
|
|
@@ -256,9 +275,9 @@ export function conformManagedBody(model, emVersion) {
|
|
|
256
275
|
MODEL_DIR: ${modelDir}`;
|
|
257
276
|
}
|
|
258
277
|
function conformWorkflowHeader(model, emVersion) {
|
|
259
|
-
return `# Generated once by \`em ci init ${model}\` (em ${emVersion})
|
|
278
|
+
return `# Generated once by \`em ci init ${model}\` (em ${emVersion}) - docs/ci.md#conformance-cadence-advisory
|
|
260
279
|
#
|
|
261
|
-
# This file is yours from here: edit it freely. It is advisory-only by construction
|
|
280
|
+
# This file is yours from here: edit it freely. It is advisory-only by construction - the job
|
|
262
281
|
# never fails the build on drift (findings become a GitHub issue for a human to ratify), and
|
|
263
282
|
# the state file's \`Last conformance:\` marker only advances when a human ratifies the run's
|
|
264
283
|
# outcome locally and commits that update. Resist wiring this to every push; cadence, not
|
|
@@ -281,6 +300,41 @@ export function buildConformWorkflowFile(model, emVersion) {
|
|
|
281
300
|
const { start, end } = markerPair(CONFORM_WORKFLOW_MARKER, "hash");
|
|
282
301
|
return `${conformWorkflowHeader(model, emVersion)}${start}\n${conformManagedBody(model, emVersion)}\n${end}\n`;
|
|
283
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Which model an existing managed block was generated for, read from the block's own content
|
|
305
|
+
* (MIL-256). Deliberately content-derived rather than a new marker line: blocks written by em
|
|
306
|
+
* 1.13.0 and earlier carry no machine-readable model, but every one of them bakes the model into
|
|
307
|
+
* the per-model `run:` lines (em-ci.yml: `slice index "<model>" --check`; em-conform.yml:
|
|
308
|
+
* `MODEL_DIR: <dir>`). Neither pattern includes the `npx @milehimikey/em@<ver>` pin, so a
|
|
309
|
+
* version difference can never read as a different model. Returns null when the block was
|
|
310
|
+
* hand-edited past recognition - callers then treat it as the same model (the conservative,
|
|
311
|
+
* pre-MIL-256 behavior) rather than refusing on a guess.
|
|
312
|
+
*/
|
|
313
|
+
export function managedBlockModel(markerName, blockBody) {
|
|
314
|
+
if (markerName === CONFORM_WORKFLOW_MARKER) {
|
|
315
|
+
const m = /^\s*MODEL_DIR: (.+?)\s*$/m.exec(blockBody);
|
|
316
|
+
return m ? m[1] : null;
|
|
317
|
+
}
|
|
318
|
+
const m = /\bslice index "([^"]*)" --check\b/.exec(blockBody);
|
|
319
|
+
return m ? m[1] : null;
|
|
320
|
+
}
|
|
321
|
+
function existingBlockBody(content, markerName) {
|
|
322
|
+
const m = markerRegex(markerName, "hash").exec(content);
|
|
323
|
+
return m ? m[2] : null;
|
|
324
|
+
}
|
|
325
|
+
/** `--force` over another model's block also retargets the generated header's own wording
|
|
326
|
+
* (`em ci init <model>` on `#` comment lines above the start marker) at the new model, taken
|
|
327
|
+
* from the freshly generated header. Only those exact generated phrasings are touched; a
|
|
328
|
+
* header the repo reworded, and everything below the start marker, is left alone. */
|
|
329
|
+
function retargetHeader(content, generated, markerName) {
|
|
330
|
+
const startLine = markerPair(markerName, "hash").start;
|
|
331
|
+
const at = content.indexOf(startLine);
|
|
332
|
+
const nextModel = /^#.*`em ci init ([^ `]+)/m.exec(generated)?.[1];
|
|
333
|
+
if (at < 0 || !nextModel)
|
|
334
|
+
return content;
|
|
335
|
+
const head = content.slice(0, at).replace(/^(#.*`em ci init )([^ `]+)/gm, (_w, pre) => `${pre}${nextModel}`);
|
|
336
|
+
return head + content.slice(at);
|
|
337
|
+
}
|
|
284
338
|
/**
|
|
285
339
|
* Decide what to do with one generated file: create it if missing, patch the managed block if
|
|
286
340
|
* the marker pair is present, or (without `force`) leave an existing unmarked file alone. Pure
|
|
@@ -290,6 +344,12 @@ export function buildConformWorkflowFile(model, emVersion) {
|
|
|
290
344
|
* marker-wrapped body) — written verbatim for `create`/`would-replace`. `managedBody` is just
|
|
291
345
|
* the text between the markers, reused to patch an existing marked file in place without
|
|
292
346
|
* touching whatever a repo added around it.
|
|
347
|
+
*
|
|
348
|
+
* MIL-256 (#174): the managed block is single-model (multi-model is MIL-233). A marked file
|
|
349
|
+
* whose block was generated for a DIFFERENT model than `managedBody` is `other-model` (refused)
|
|
350
|
+
* unless `force`, then `replace-model`. The same model with any other difference (version pin,
|
|
351
|
+
* old text) stays `stale`, exactly as before. `em upgrade`'s ci-block step re-derives the model
|
|
352
|
+
* from the existing file itself, so it only ever plans the same model.
|
|
293
353
|
*/
|
|
294
354
|
export function planCiFile(path, generated, managedBody, markerName, force) {
|
|
295
355
|
if (!existsSync(path))
|
|
@@ -299,10 +359,20 @@ export function planCiFile(path, generated, managedBody, markerName, force) {
|
|
|
299
359
|
if (updated === null) {
|
|
300
360
|
return force ? { kind: "would-replace", content: generated } : { kind: "missing-markers" };
|
|
301
361
|
}
|
|
302
|
-
|
|
362
|
+
if (updated === original)
|
|
363
|
+
return { kind: "ok", content: original };
|
|
364
|
+
const existing = existingBlockBody(original, markerName);
|
|
365
|
+
const previous = existing === null ? null : managedBlockModel(markerName, existing);
|
|
366
|
+
const next = managedBlockModel(markerName, managedBody);
|
|
367
|
+
if (previous !== null && next !== null && previous !== next) {
|
|
368
|
+
if (!force)
|
|
369
|
+
return { kind: "other-model", previous };
|
|
370
|
+
return { kind: "replace-model", content: retargetHeader(updated, generated, markerName), previous };
|
|
371
|
+
}
|
|
372
|
+
return { kind: "stale", content: updated, current: original };
|
|
303
373
|
}
|
|
304
374
|
export function applyCiFile(path, status) {
|
|
305
|
-
if (status.kind === "create" || status.kind === "stale" || status.kind === "would-replace") {
|
|
375
|
+
if (status.kind === "create" || status.kind === "stale" || status.kind === "would-replace" || status.kind === "replace-model") {
|
|
306
376
|
writeFileSync(path, status.content, "utf8");
|
|
307
377
|
}
|
|
308
378
|
}
|