@milehimikey/em 1.11.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.
- package/.claude/skills/event-modeling/SKILL.md +4 -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 +22 -6
- package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
- package/.claude/skills/event-modeling-implement/SKILL.md +12 -6
- package/.claude/skills/event-modeling-implement/reference/implement.md +165 -18
- package/.claude/skills/event-modeling-review/SKILL.md +5 -1
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +57 -8
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +13 -3
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +69 -0
- package/.claude/skills/event-modeling-shared/templates/conformance-report.md +8 -1
- package/.claude/skills/event-modeling-shared/templates/constitution.md +8 -1
- package/.claude/skills/event-modeling-shared/templates/state.md +3 -0
- package/README.md +1 -0
- package/dist/catalog/build.js +42 -1
- package/dist/catalog/build.js.map +1 -1
- package/dist/catalog/coverage.d.ts +15 -0
- package/dist/catalog/coverage.js +34 -0
- package/dist/catalog/coverage.js.map +1 -0
- package/dist/catalog/docJoin.d.ts +20 -2
- package/dist/catalog/docJoin.js +32 -4
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/docModelConsistencyValidate.js +1 -1
- package/dist/catalog/docModelConsistencyValidate.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/noteBindingValidate.js +18 -0
- package/dist/catalog/noteBindingValidate.js.map +1 -1
- package/dist/catalog/orphanedSliceDocValidate.d.ts +8 -1
- package/dist/catalog/orphanedSliceDocValidate.js +33 -6
- package/dist/catalog/orphanedSliceDocValidate.js.map +1 -1
- package/dist/catalog/pages.d.ts +16 -1
- package/dist/catalog/pages.js +16 -4
- package/dist/catalog/pages.js.map +1 -1
- 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/catalog/sliceReadyValidate.d.ts +12 -1
- package/dist/catalog/sliceReadyValidate.js +4 -4
- package/dist/catalog/sliceReadyValidate.js.map +1 -1
- package/dist/cli/agentsMd.js +9 -3
- package/dist/cli/agentsMd.js.map +1 -1
- package/dist/cli/ciInit.js +20 -0
- package/dist/cli/ciInit.js.map +1 -1
- package/dist/cli/conformScope.d.ts +1 -0
- package/dist/cli/conformScope.js +13 -4
- 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 +34 -9
- package/dist/cli/coverage.js +61 -24
- 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/markImplemented.js +12 -1
- package/dist/cli/markImplemented.js.map +1 -1
- 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/ratify.js +17 -1
- package/dist/cli/ratify.js.map +1 -1
- package/dist/cli/reratify.d.ts +17 -0
- package/dist/cli/reratify.js +37 -2
- package/dist/cli/reratify.js.map +1 -1
- package/dist/cli/review.js +12 -1
- package/dist/cli/review.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 +24 -7
- package/dist/cli/sliceIndex.js.map +1 -1
- package/dist/cli/sliceLink.js +16 -0
- package/dist/cli/sliceLink.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 +90 -1
- package/dist/cli/status.js +151 -16
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/upgrade.d.ts +122 -0
- package/dist/cli/upgrade.js +476 -0
- package/dist/cli/upgrade.js.map +1 -0
- package/dist/cli.js +586 -66
- package/dist/cli.js.map +1 -1
- package/dist/emit/coverageJson.d.ts +2 -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 +34 -1
- package/dist/emit/json.js +84 -2
- 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 +22 -1
- package/dist/emit/statusJson.js.map +1 -1
- package/dist/emit/upgradeJson.d.ts +5 -0
- package/dist/emit/upgradeJson.js +26 -0
- package/dist/emit/upgradeJson.js.map +1 -0
- package/dist/emit/validateJson.d.ts +10 -5
- package/dist/emit/validateJson.js +22 -8
- package/dist/emit/validateJson.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +151 -22
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/continuation.d.ts +23 -0
- package/dist/model/continuation.js +49 -0
- package/dist/model/continuation.js.map +1 -0
- package/dist/model/queryIndex.js +1 -1
- package/dist/model/queryIndex.js.map +1 -1
- package/dist/model/rules.d.ts +13 -0
- package/dist/model/rules.js +18 -0
- package/dist/model/rules.js.map +1 -1
- package/dist/model/sliceDocIndex.d.ts +12 -5
- package/dist/model/sliceDocIndex.js +18 -7
- package/dist/model/sliceDocIndex.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/render/sliceStatus.d.ts +3 -3
- package/dist/render/sliceStatus.js +27 -29
- package/dist/render/sliceStatus.js.map +1 -1
- package/dist/templates.d.ts +3 -3
- package/dist/templates.js +15 -2
- package/dist/templates.js.map +1 -1
- package/dist/util/localDate.d.ts +2 -0
- package/dist/util/localDate.js +17 -0
- package/dist/util/localDate.js.map +1 -0
- 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 +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.13.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use when the user runs `/event-modeling` (with or without a phase name), or wants to resume an
|
|
6
6
|
event-modeling session without specifying which part of the process applies. Reads the model's
|
|
@@ -30,7 +30,9 @@ phase lives in.
|
|
|
30
30
|
## Routing logic
|
|
31
31
|
|
|
32
32
|
1. Check the tool: `em --version`. If missing, tell the user to run `npm i -g @milehimikey/em`
|
|
33
|
-
and stop until installed.
|
|
33
|
+
and stop until installed. Then check this vendored bundle's own currency against it — see
|
|
34
|
+
`../event-modeling-shared/reference/operating-principles.md`'s Preconditions step 2: a bundle
|
|
35
|
+
more than a minor version stale is a STOP, not a warning.
|
|
34
36
|
2. Parse `$ARGUMENTS` for a phase name.
|
|
35
37
|
- **A recognized phase name** (`discover`, `extract`, `model`, `slice`, `implement`,
|
|
36
38
|
`conform`, `validate`, `watch`, `review`): look it up in the table above and invoke that
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling-conform
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.13.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use when checking a ratified event model (and its slice docs) for drift against the codebase
|
|
6
6
|
that implements it — advisory only, never a gate, never an unprompted edit — or when walking
|
|
@@ -33,18 +33,29 @@ or doc; write the as-is picture into that scratch model (`<model-name>-asis.em`,
|
|
|
33
33
|
canonical model's names wherever the code matches them); run `em diff <model-name>.em
|
|
34
34
|
<model-name>-asis.em --json` and let `em` decide the structural deltas; classify every finding
|
|
35
35
|
(real drift / model gap / internal inconsistency / uncertainty) with cited evidence; write
|
|
36
|
-
`conformance/<date>-report.md`
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
`conformance/<date>-report.md` **and its structured twin `conformance/<date>-findings.json`**
|
|
37
|
+
(one entry per finding, `em conform-findings check <path>` to verify) with proposed `issue
|
|
38
|
+
"conformance: …"` red notes; apply only the proposals the user ratifies, then re-render and
|
|
39
|
+
validate; run `em state set-conformance <revision> --report <path>` to update the state file's
|
|
40
|
+
`Last conformance:` marker.
|
|
39
41
|
|
|
40
42
|
Ruling on a finding is a human gate, like ratifying a slice
|
|
41
43
|
(docs/process.md#the-slice-lifecycle-gates): the sweep gathers evidence and proposes, a person
|
|
42
|
-
decides, and `em
|
|
44
|
+
decides, and `em conform-supersede ... --locus <model|doc|code|none> --by <name>` records the
|
|
45
|
+
ruling on the findings record (and stamps the report) — never rule on your own judgment. Once
|
|
46
|
+
every finding touching a slice is ruled, `em slice conform <model-name>.em <key> --at <rev>`
|
|
47
|
+
certifies that version of the slice.
|
|
43
48
|
|
|
44
|
-
End of phase: state file's `Last conformance:` marker updated (via `em state set-conformance`
|
|
45
|
-
|
|
46
|
-
proposals were applied. Conform doesn't chain to
|
|
47
|
-
again whenever the codebase has moved.
|
|
49
|
+
End of phase: state file's `Last conformance:` marker updated (via `em state set-conformance` —
|
|
50
|
+
refuses while any `implemented` slice still has an unruled finding; `--partial` escapes with a
|
|
51
|
+
loud notice), Decisions log entry if any proposals were applied. Conform doesn't chain to
|
|
52
|
+
another phase — it's a recurring loop, run again whenever the codebase has moved.
|
|
53
|
+
|
|
54
|
+
A full (non-`--partial`) `em state set-conformance` also **certifies the model's current design
|
|
55
|
+
version** (MIL-218, `docs/process.md#model-versions`) — it refuses if no design version has ever
|
|
56
|
+
been bumped ("bump a model version first") or if the model changed during this run without a
|
|
57
|
+
re-bump; run `em model version bump <model-name>.em --by <name>` before the final `em state
|
|
58
|
+
set-conformance` call if either applies.
|
|
48
59
|
|
|
49
60
|
## Phase: `validate`
|
|
50
61
|
|
|
@@ -234,10 +234,22 @@ step 2 — sorts into exactly one class:
|
|
|
234
234
|
which finding. Cite `slice.doc.driftSignal` and `slice.doc.version` together as evidence.
|
|
235
235
|
- **Extraction uncertainty** — no evidence either way. Never reported as drift or a gap.
|
|
236
236
|
|
|
237
|
+
**Read-model exposure (MIL-215).** Two rulings apply the classes above to a `view`'s exposure —
|
|
238
|
+
whether it ships a query endpoint — instead of leaving each case to fresh judgment:
|
|
239
|
+
|
|
240
|
+
- An **internal view** (no `ui` reads it, and it isn't marked `public`) implemented as
|
|
241
|
+
criteria-sourced state (a reaction's own read, no materialized projection — the general idiom
|
|
242
|
+
step 2 already names) or as an unexposed projection is **Conformant, never dead code to
|
|
243
|
+
flag**. It's the read operation the model draws to say an automation or translation *must*
|
|
244
|
+
read, not an endpoint promise; don't propose deleting it.
|
|
245
|
+
- An **endpoint in code on a view the model shows no `ui` reading and no `public` marking** is a
|
|
246
|
+
**Model gap** — "add the `ui` or remove the endpoint" — **never Real drift to delete**.
|
|
247
|
+
Nobody decided whether that view is exposed; deleting the code decides it for them.
|
|
248
|
+
|
|
237
249
|
Every classification must cite the evidence recorded in step 2 (or the `em diff` entry, for
|
|
238
250
|
structural findings) — a finding with no citation isn't ready to report.
|
|
239
251
|
|
|
240
|
-
### 5. Report + proposals
|
|
252
|
+
### 5. Report + findings record + proposals
|
|
241
253
|
|
|
242
254
|
Write `conformance/<YYYY-MM-DD>-report.md` in the model directory, from
|
|
243
255
|
`../../event-modeling-shared/templates/conformance-report.md`. For a real-drift or model-gap finding, propose a
|
|
@@ -250,6 +262,71 @@ stays auditable) but don't ask the user to ratify what's already ratified. **You
|
|
|
250
262
|
canonical model or a slice doc unprompted** — walk the report with the user, apply only the
|
|
251
263
|
proposals they approve, then re-render and `em validate`.
|
|
252
264
|
|
|
265
|
+
**Also write the findings JSON (MIL-214).** Alongside the report, write
|
|
266
|
+
`conformance/<YYYY-MM-DD>-findings.json` — one entry per `### n.` finding, ids matching the
|
|
267
|
+
report's own numbering. Shape (`findingsSchemaVersion: "1.0"`, additive-only, 2-space indent,
|
|
268
|
+
keys sorted, findings sorted by `id`, trailing newline):
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"findingsSchemaVersion": "1.0",
|
|
273
|
+
"model": "checkout.em",
|
|
274
|
+
"report": "conformance/YYYY-MM-DD-report.md",
|
|
275
|
+
"revision": "<target-repo revision>",
|
|
276
|
+
"findings": [
|
|
277
|
+
{
|
|
278
|
+
"id": 1,
|
|
279
|
+
"surface": "structural",
|
|
280
|
+
"class": "Real drift",
|
|
281
|
+
"slice": "checkout",
|
|
282
|
+
"evidence": "code shows X at path/to/File.kt:42",
|
|
283
|
+
"locus": null,
|
|
284
|
+
"resolvedBy": null,
|
|
285
|
+
"resolvedOn": null
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
`surface` is one of `structural` (`.em` <-> code — step 3's diff), `spec` (slice doc <-> code —
|
|
292
|
+
step 2's invariant/scenario walk), `internal` (slice doc <-> `.em` — step 2's `doc-model-*`
|
|
293
|
+
diagnostics), or `other`. `class` is the step-4 classification verbatim (Real drift, Model gap,
|
|
294
|
+
Internal inconsistency, Accepted divergence, Unpropagated delta, Extraction uncertainty). `slice`
|
|
295
|
+
is the concerned slice's export key, or `null` for a finding that isn't attributable to one
|
|
296
|
+
(counts as in-scope for every slice's unruled-findings check). `locus`/`resolvedBy`/`resolvedOn`
|
|
297
|
+
start `null` — a later `em conform-supersede --locus --by` call fills them in once a human rules.
|
|
298
|
+
Run `em conform-findings check conformance/YYYY-MM-DD-findings.json` before ending the session —
|
|
299
|
+
a headless run's mechanical proof it wrote a valid record.
|
|
300
|
+
|
|
301
|
+
**Ruling on a finding (MIL-214) — once the user decides.** Run, per finding or batch of findings
|
|
302
|
+
ruled the same way:
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
em conform-supersede <model-name>.em conformance/YYYY-MM-DD-report.md --as-of <target-repo revision> \
|
|
306
|
+
--findings <spec> --locus <model|doc|code|none> --by <name>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
— this records `locus`/`resolvedBy`/`resolvedOn` on the named findings in the JSON (refusing to
|
|
310
|
+
silently overwrite an already-different `locus`) AND stamps the report's "superseded as of `<rev>`"
|
|
311
|
+
banner in the same call (see below). `locus: code` on an unresolved finding is the carrier for
|
|
312
|
+
"the spec is right, the code is wrong" — no doc delta required for that case any more; `locus:
|
|
313
|
+
model` means fix the `.em` (apply the proposed red note, or a stronger edit the user chose
|
|
314
|
+
instead); `locus: doc` means the slice doc needs a delta/re-ratification; `locus: none` covers
|
|
315
|
+
Accepted divergence/Unpropagated delta/Extraction uncertainty, where nothing needs to change.
|
|
316
|
+
|
|
317
|
+
**Certifying a slice, once every finding touching it is ruled (MIL-214).** For each in-scope
|
|
318
|
+
slice with no unruled findings left:
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
em slice conform <model-name>.em <slice-key> --at <target-repo revision>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
— records `conformedVersion`/`conformedAt`/`conformedOn` on that slice's doc, so
|
|
325
|
+
`driftSignal` reads `in-sync` again (it reads `uncertified` for an implemented slice that's never
|
|
326
|
+
been through this) instead of just trusting `implementedIn`. Legal only for `status: implemented`
|
|
327
|
+
with a link, and itself refuses if an unruled finding for that slice still exists at this
|
|
328
|
+
revision — the mechanical proof this step comes only after rulings, not before.
|
|
329
|
+
|
|
253
330
|
End of run: update the state file's `Last conformance:` marker with
|
|
254
331
|
|
|
255
332
|
```
|
|
@@ -258,14 +335,21 @@ em state set-conformance <target-repo revision> --report conformance/YYYY-MM-DD-
|
|
|
258
335
|
|
|
259
336
|
— the revision is the one you just diffed against, so the next run's scope starts from here.
|
|
260
337
|
The command writes the exact format step 1 parses back out; never hand-edit the bullet.
|
|
338
|
+
**Refuses (MIL-214) while any `implemented` slice still has an unruled finding** in the findings
|
|
339
|
+
JSON beside the report — rule on every finding first (or certify what's already ruled via `em
|
|
340
|
+
slice conform` above), or pass `--partial` to record the marker with a loud notice instead
|
|
341
|
+
(`notice: conformance marker recorded as PARTIAL — N finding(s) unruled`) when the team
|
|
342
|
+
deliberately wants to advance the marker without finishing every ruling this session.
|
|
261
343
|
**The marker only advances with the human in the loop**: in an interactive session, run it
|
|
262
|
-
after walking the report with the user; a headless/scheduled run writes the report
|
|
263
|
-
the marker (nobody ratified the outcome), so the next run re-walks the same span —
|
|
264
|
-
`docs/ci.md`. If any proposals were ratified and applied, log a Decisions entry noting what
|
|
344
|
+
after walking the report with the user; a headless/scheduled run writes the report and findings
|
|
345
|
+
JSON but never the marker (nobody ratified the outcome), so the next run re-walks the same span —
|
|
346
|
+
see `docs/ci.md`. If any proposals were ratified and applied, log a Decisions entry noting what
|
|
265
347
|
changed and why.
|
|
266
348
|
|
|
267
|
-
**Superseding the report you just walked (MIL-164).**
|
|
268
|
-
|
|
349
|
+
**Superseding the report you just walked (MIL-164).** The `--locus`/`--by` call above already
|
|
350
|
+
stamps the report's banner in the same call when a ruling was recorded; if a finding's ruling
|
|
351
|
+
doesn't need `--locus`/`--by` recorded (a migration-path report predating the findings JSON, or
|
|
352
|
+
a ruling you're only banner-stamping after the fact), run the same command without them:
|
|
269
353
|
|
|
270
354
|
```
|
|
271
355
|
em conform-supersede <model-name>.em conformance/YYYY-MM-DD-report.md --as-of <target-repo revision> --findings <spec>
|
|
@@ -298,10 +382,23 @@ each call adds its own stamp rather than overwriting the last.
|
|
|
298
382
|
## Completion & handoff
|
|
299
383
|
|
|
300
384
|
Conform is complete when: every in-scope slice has been walked (step 2), the scratch model has
|
|
301
|
-
been diffed (step 3), every finding is classified with cited evidence (step 4), the report
|
|
302
|
-
written (step 5), the state file's `Last conformance:` marker is updated
|
|
303
|
-
|
|
304
|
-
|
|
385
|
+
been diffed (step 3), every finding is classified with cited evidence (step 4), the report AND
|
|
386
|
+
its findings JSON are written (step 5), the state file's `Last conformance:` marker is updated
|
|
387
|
+
(or deliberately left as `--partial`), and the user has seen the report and said which proposals
|
|
388
|
+
(if any) to apply and which findings resolve to which locus. Applied proposals get re-rendered
|
|
389
|
+
and re-validated before you call the run done.
|
|
390
|
+
|
|
391
|
+
A full (non-`--partial`) `em state set-conformance` does one more thing (MIL-218): it also
|
|
392
|
+
**certifies** the model's current design version — writes `certified: { at, on, report,
|
|
393
|
+
findings }` into that version's `model-versions/v<N>.json` manifest, and rewrites the state
|
|
394
|
+
file's `Certified:` bullet. This is the "conform ran clean against version N" fact a
|
|
395
|
+
stakeholder or a release note can point at. It only works on an already-**bumped** design
|
|
396
|
+
version — `em state set-conformance` refuses (pointing at `em model version bump`) if no
|
|
397
|
+
manifest exists yet, or if the model has drifted since the last bump (the certification would
|
|
398
|
+
otherwise name a version that isn't what conform actually walked). If the model changed during
|
|
399
|
+
this conform run (a slice's `version:` moved, or the `.em` file itself changed), bump a new
|
|
400
|
+
design version (`em model version bump --by <name>`) before the final `em state
|
|
401
|
+
set-conformance` call. `--partial` never certifies. See `docs/model-versions.md`.
|
|
305
402
|
|
|
306
403
|
Conform doesn't chain to another phase automatically — it's a recurring loop, not a step in
|
|
307
404
|
building the model. Suggest running it again next time the target codebase has moved, at
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling-design
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.13.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use when structuring a draft event model into swimlanes and the four patterns (State Change,
|
|
6
6
|
State View, Automation, Translation), evaluating a model's structural completeness, or writing
|
|
@@ -40,15 +40,22 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
|
|
|
40
40
|
slice, none skipped: `em slice new "<slice name>" --pattern <state-change|state-view|
|
|
41
41
|
automation|translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes
|
|
42
42
|
`slices/<slice-name>.md` at `status: draft` (the same command, flags, and `--wire`
|
|
43
|
-
note-binding the `slice` phase's step 2 below documents in full).
|
|
44
|
-
|
|
43
|
+
note-binding the `slice` phase's step 2 below documents in full). A slice whose only element
|
|
44
|
+
is a `view X again` instance is a **continuation** (MIL-208), not a slice to scaffold —
|
|
45
|
+
`--wire` refuses it and names the originating slice holding `X`'s first declaration; its
|
|
46
|
+
scenarios go into that doc instead. Then, for each freshly scaffolded doc, hold a first-pass
|
|
47
|
+
Socratic pass and write the happy-path Given/When/Then into
|
|
45
48
|
its `## Scenarios (Given / When / Then)` section and the obvious invariants known so far into
|
|
46
49
|
its `## Invariants / Business Rules` section — **into the doc, never into the state file.**
|
|
47
50
|
Keep this pass shallow (no field tables, alternate/error flows, or NFRs yet — that's the full
|
|
48
51
|
deep-dive in the `slice` phase below); the docs are the home for everything collected from
|
|
49
52
|
here on, not yet implementation-ready specs. Because every doc already exists by the time the
|
|
50
53
|
`slice` phase begins, its own step 2 scaffolding call is then a no-op — that phase picks up
|
|
51
|
-
at its step 1's deeper pass.
|
|
54
|
+
at its step 1's deeper pass. For a purely exploratory/backbone-mapping model that only needs
|
|
55
|
+
status coloring right now (not a full spec per slice yet), `em slice stub-all <model>.em` is
|
|
56
|
+
the one-command fast path: a near-free stub per undocumented slice, wired, in slice order
|
|
57
|
+
(MIL-184) — skip the per-slice `em slice new` calls above and deepen individual docs later
|
|
58
|
+
when the team is ready.
|
|
52
59
|
3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
|
|
53
60
|
half-slice — **every command has something that triggers it**, every command emits an event,
|
|
54
61
|
**every event is read by a read model**, **every read model has a consumer**, every view has a
|
|
@@ -60,6 +67,10 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
|
|
|
60
67
|
For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
|
|
61
68
|
this fact and what they do with it. The honest answers are "here's the read model we missed" or
|
|
62
69
|
"nobody — so why are we recording it", and both improve the model.
|
|
70
|
+
**Per `view`, also ask who reads it (MIL-215):** a person or client reads it → draw the `ui`
|
|
71
|
+
that reads it; another model/system reads it → mark it `public`; only an automation reads
|
|
72
|
+
it → nothing, it's internal by design. No new keyword — reachability is already what `ui` and
|
|
73
|
+
`public` mean.
|
|
63
74
|
|
|
64
75
|
End of phase: render, update state (`em state set-phase slice` — the mechanical marker the state
|
|
65
76
|
machine expects), and stop. Every slice already has a draft doc ready to receive the deep spec,
|
|
@@ -115,6 +126,9 @@ For each slice:
|
|
|
115
126
|
"slices/<slice-name>.md"` line straight into the `.em`, onto the slice's primary element (the
|
|
116
127
|
command for State Change, the view for State View, the processor for Automation, the
|
|
117
128
|
translation for Translation), matched by export key — see step 4 for when it refuses instead.
|
|
129
|
+
A slice whose sole candidate view is a later `view X again` instance is a **continuation**
|
|
130
|
+
(MIL-208): `--wire` refuses it and names the originating slice holding `X`'s first
|
|
131
|
+
declaration — write this slice's scenarios into that doc instead of scaffolding a new one.
|
|
118
132
|
Both `--pattern` and `--swimlane` are required; an invalid `--pattern` is refused with the
|
|
119
133
|
valid choices listed. Never hand-type this block, and never fall back to a placeholder
|
|
120
134
|
pattern/swimlane to dodge the flags. Then fill in every judgment section below the stub from
|
|
@@ -154,5 +168,7 @@ Before the project's first slice is implemented it also needs a ratified impleme
|
|
|
154
168
|
constitution (stack, code style, testing norms, NFR baselines, review norms) — the
|
|
155
169
|
`event-modeling-implement` skill elicits it, see its `reference/implement.md` §7.
|
|
156
170
|
|
|
157
|
-
End of phase: every slice that should exist has a doc
|
|
158
|
-
|
|
171
|
+
End of phase: every slice that should exist has a doc — except a continuation slice (MIL-208, a
|
|
172
|
+
later `view X again` instance), which has none by design and is documented by the originating
|
|
173
|
+
slice instead — `README.md`'s Slices table is current, and the model validates clean. Suggest
|
|
174
|
+
`event-modeling-implement` for each ratified slice.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling-discover
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.13.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use when starting a brand-new event model from scratch — greenfield discovery: brainstorming
|
|
6
6
|
past-tense events, storyboarding the happy path, finding the commands and read models — or when
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling-implement
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.13.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use when building a single ratified event-modeling slice (status: ready-to-implement) into
|
|
6
6
|
merged, tested code — the one phase where the agent builds rather than facilitates. Gates on
|
|
@@ -16,11 +16,17 @@ the agent builds rather than facilitates.
|
|
|
16
16
|
**Read `reference/implement.md` before doing any implement work and follow it as the
|
|
17
17
|
contract.** In short: gate on `em validate <model>.em --slice-ready <key>` (stop and hand
|
|
18
18
|
back if it fails — never edit the doc to make the gate pass); read the project's implementation
|
|
19
|
-
constitution before writing code and stay inside its rules;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
constitution before writing code and stay inside its rules; before a project's first slice PR,
|
|
20
|
+
land a one-time foundation PR holding every model event (plus the shared interface shell) so
|
|
21
|
+
slices only ever import contracts, never invent or reach into a sibling's; build in dependency
|
|
22
|
+
order from `em query upstream`, not timeline order; treat the slice doc as
|
|
23
|
+
the spec (read-only, except the merge-time `status`/`implementedIn` flip); implement exactly one
|
|
24
|
+
slice doc per PR — a `covers:` doc's two keys ship together in that one PR, an `again` view
|
|
25
|
+
instance gets its own; ship the endpoint a
|
|
26
|
+
`ui`-triggered command or `ui`-consumed view obligates, and surface — never invent — one the
|
|
27
|
+
model doesn't show; surface every gap to the user instead of deciding it silently; cover every
|
|
28
|
+
`INV-<MNEMONIC>-n` and every scenario with tests; in spec-kit projects, allocate via
|
|
29
|
+
em-sdd-bridge (redirect mode) and never run `/speckit.specify`.
|
|
24
30
|
|
|
25
31
|
**Gate: no constitution, no first slice.** The constitution is the project's ratified house rules
|
|
26
32
|
— stack and architectural shape (including which implementation skill each slice pattern routes
|
|
@@ -26,18 +26,23 @@ em validate <model>.em --slice-ready <slice-key> --json
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
`<slice-key>` is the slice's export key — the kebab-case slug of its name (`"Place Order"` →
|
|
29
|
-
`place-order`).
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
implementation
|
|
29
|
+
`place-order`). Don't guess it or hand-slug the name yourself: list every slice's key with
|
|
30
|
+
`em export <model>.em | jq -r '.model.slices[].key'`. Read the JSON document's `ready` field —
|
|
31
|
+
don't infer it from the exit code or any printed text. `ready: true` means: the slice has a doc
|
|
32
|
+
bound via `note "slices/<key>.md"`, its frontmatter is usable, `status: ready-to-implement`,
|
|
33
|
+
every `## Open Questions` checkbox is checked, and no status/version/link incoherence is flagged
|
|
34
|
+
— `gates` names each of those 4 conditions individually (`docBound`/`frontmatterUsable`/
|
|
35
|
+
`statusReady`/`noUncheckedOpenQuestions`) if you need to say which one is blocking.
|
|
36
|
+
|
|
37
|
+
**`ready: false` means stop** — with one named exception: a slice already `status: implemented`
|
|
38
|
+
with `driftSignal: in-sync` or `uncertified` (MIL-214) also reads `ready: false` (its
|
|
39
|
+
`statusReady` gate fails because nothing is newly ratified), and that specific case is
|
|
40
|
+
re-implementation/repair, not a blocker — see §3's third mode before treating it as one. For
|
|
41
|
+
every other `false`, report which `gates`
|
|
42
|
+
entries are `false` (and any `diagnostics` entries concerning this slice) and hand the slice
|
|
43
|
+
back to the humans. Never make the gate pass yourself — checking an open-question box, flipping
|
|
44
|
+
`status`, or editing frontmatter are ratification decisions, and ratification happens in a
|
|
45
|
+
facilitated session, not in an implementation branch.
|
|
41
46
|
|
|
42
47
|
## 2. Read the spec
|
|
43
48
|
|
|
@@ -68,18 +73,44 @@ Then read `slices/<slice-key>.md` end to end — every section is load-bearing:
|
|
|
68
73
|
| `## Non-Functional Requirements` | Authz, PII, performance — implement or consciously surface, never skip |
|
|
69
74
|
| `## Dependencies & Read Models Affected` | The blast radius — what else to check before and after |
|
|
70
75
|
|
|
76
|
+
**Interface obligations follow from the model, not from the doc's prose.** The DSL already
|
|
77
|
+
encodes what's reachable — see `em-dsl.md`'s "Headless / API systems & repeated read models"
|
|
78
|
+
section — and the routing from it is mechanical, not a technical choice you make while coding:
|
|
79
|
+
|
|
80
|
+
- A **command with a `ui` trigger** ships the endpoint that dispatches it as part of this slice
|
|
81
|
+
— shape, verb, status codes, and error mapping follow the constitution's interface conventions
|
|
82
|
+
(§7). Because: the model already said this command is reachable from outside; leaving it
|
|
83
|
+
reachable only through an internal gateway silently narrows what the model ratified.
|
|
84
|
+
- A **view with a `ui` consumer** ships the query endpoint as part of this slice, same
|
|
85
|
+
conventions.
|
|
86
|
+
- **No `ui`** on the trigger or the consumer (a command dispatched by a processor or a
|
|
87
|
+
translation, a view read only by a reaction) means internal-only: no endpoint. If one seems
|
|
88
|
+
needed anyway, that's a gap to surface (§4), not a technical choice to make mid-implementation
|
|
89
|
+
— adding a route the model doesn't show is exactly the kind of silent divergence §4 exists to
|
|
90
|
+
catch. An endpoint on a view the model shows no `ui` reading is a gap to surface the same way
|
|
91
|
+
(MIL-215) — conform will classify it as a **model gap**, never drift to delete, once you flag
|
|
92
|
+
it (`../../event-modeling-conform/reference/conform.md`, Flow step 4).
|
|
93
|
+
- **`public` events and views** are the model's published integration surface: their shape is a
|
|
94
|
+
contract, not an implementation detail. `em validate --list-public` lists them; `em typespec`
|
|
95
|
+
is the POC generator for a downstream schema. Changing a public element's shape is a ratified
|
|
96
|
+
delta, never an implementation-time choice.
|
|
97
|
+
|
|
71
98
|
For timeline context, read the slice's surroundings in the `.em` (what triggers it, what
|
|
72
99
|
consumes its output) — the slice's own diagram (`slices/<slice-key>.svg`) shows its canonical
|
|
73
100
|
pattern shape. For machine-readable facts, use `em export <model>.em --slice <slice-key>`
|
|
74
101
|
rather than parsing the DSL or the doc's frontmatter yourself: it returns just this slice's
|
|
75
102
|
object — `pattern`, element refs, fields, and the joined doc metadata (`slice.doc.status`,
|
|
76
103
|
`version`, `driftSignal`) — without piping the whole model's export to find one `slice.doc`.
|
|
104
|
+
When the view repeats later via `again` (MIL-208), that same call on the *originating* slice
|
|
105
|
+
also returns `alsoReads: [{ event, atSlice }]` — the union of every event the read model
|
|
106
|
+
consumes across all its later continuations, in timeline order — so implementing it never means
|
|
107
|
+
walking each continuation by hand to assemble the full event list.
|
|
77
108
|
|
|
78
109
|
If the project ships pattern-specific implementation skills (for example an Axon/DCB skill
|
|
79
110
|
set keyed on a slice doc's `pattern:` frontmatter), route by the slice's pattern and follow
|
|
80
111
|
those.
|
|
81
112
|
|
|
82
|
-
## 3.
|
|
113
|
+
## 3. Three modes: first version, ratified delta, re-implementation/repair
|
|
83
114
|
|
|
84
115
|
Check the doc's `version:` and `## Delta` section (or `em export`'s `slice.doc.driftSignal`):
|
|
85
116
|
|
|
@@ -93,6 +124,29 @@ Check the doc's `version:` and `## Delta` section (or `em export`'s `slice.doc.d
|
|
|
93
124
|
**Never regenerate merged code wholesale from the model** — after merge, code is owned by
|
|
94
125
|
the people who've edited it (generated-then-owned); regeneration is legal only for slices
|
|
95
126
|
whose code never merged.
|
|
127
|
+
- **Re-implementation / repair** (`status: implemented`, `implementedIn` set, `driftSignal:
|
|
128
|
+
in-sync` **or** `uncertified` (MIL-214 — certification status is orthogonal to whether the doc
|
|
129
|
+
changed; most shipped slices sit at `uncertified` until a conform sweep certifies them, which
|
|
130
|
+
doesn't change which mode this is) — `--slice-ready` reports `ready: false` purely because
|
|
131
|
+
`statusReady` fails, with no other gate false and no doc content changed): the doc hasn't
|
|
132
|
+
changed, but the shipped code
|
|
133
|
+
needs rebuilding or fixing — deleted code, a reverted PR, a bug in behavior the doc already
|
|
134
|
+
specifies. **The doc is still the spec.** The gate's `false` here means "nothing new is
|
|
135
|
+
ratified," not "stop" — it is not license to self-initiate: proceed only when a human has
|
|
136
|
+
asked for this specific re-work. Scope the change to what the repair needs, exactly like a
|
|
137
|
+
delta (generated-then-owned still applies — don't regenerate the whole slice wholesale just
|
|
138
|
+
because the gate reads oddly); if what you'd change is really a change in behavior rather than
|
|
139
|
+
a repair of it, that's a ratified delta (the bullet above), not this mode — when unsure which
|
|
140
|
+
one you're in, ask the human rather than guess.
|
|
141
|
+
|
|
142
|
+
No lifecycle flip is needed unless `implementedIn` changes. If it does,
|
|
143
|
+
`em slice mark-implemented <model>.em <slice-key> <new-pr-url>` will **refuse** rather than
|
|
144
|
+
overwrite: a doc already `implemented` with a different `implementedIn` URL is left untouched
|
|
145
|
+
and the command reports the conflict (`src/cli/markImplemented.ts`) instead of silently
|
|
146
|
+
replacing provenance. That refusal is correct, not a bug to route around — never hand-edit the
|
|
147
|
+
frontmatter to force it through. Surface the conflict to the human instead: they decide whether
|
|
148
|
+
the new PR supersedes the recorded one (and re-run `mark-implemented` themselves, or ask you to,
|
|
149
|
+
once they've decided) or whether the existing link should stand.
|
|
96
150
|
|
|
97
151
|
## 4. Gaps: propose, never decide
|
|
98
152
|
|
|
@@ -114,6 +168,20 @@ undocumented ordering, a contradiction with adjacent code. The discipline:
|
|
|
114
168
|
|
|
115
169
|
## 5. Definition of done
|
|
116
170
|
|
|
171
|
+
**The unit is the slice doc: one slice doc = one PR.** A doc that `covers:` another slice (an
|
|
172
|
+
automation's to-do-list view and its reaction, MIL-121) is one spec, not two — it ships in one
|
|
173
|
+
PR, and `mark-implemented` runs for both keys against the same URL. A read model is one doc,
|
|
174
|
+
even when a `view X again` clause repeats it across later positions on the timeline (MIL-208):
|
|
175
|
+
the later instance is a **continuation** of the slice holding `X`'s first declaration, not a
|
|
176
|
+
spec unit of its own — no doc, no status, no ratification, no PR of its own. Its behavior
|
|
177
|
+
belongs in the originating doc's `## Scenarios` (one scenario per event the later instance
|
|
178
|
+
feeds), not a doc of its own; `em export --slice <originating-key>` lists the full union of
|
|
179
|
+
events the read model consumes across every position via `alsoReads` (§2). Never fold multiple
|
|
180
|
+
slice docs' implementations into one PR because they touch the same read model, ship together,
|
|
181
|
+
or seem small — see §10.
|
|
182
|
+
|
|
183
|
+
- The PR description cites exactly one slice doc (plus its `covers:` keys, if any) and lists
|
|
184
|
+
that doc's invariants alongside the tests that cover them.
|
|
117
185
|
- Every `INV-<MNEMONIC>-n` invariant has at least one test that cites its ID — checked mechanically by
|
|
118
186
|
`em coverage <model>.em --tests <dir>` (MIL-130); run it (add `--strict` in CI) rather than
|
|
119
187
|
eyeballing citations by hand.
|
|
@@ -122,11 +190,15 @@ undocumented ordering, a contradiction with adjacent code. The discipline:
|
|
|
122
190
|
- Alternate/error flows (idempotency included) are covered by tests.
|
|
123
191
|
- The build and full test suite are green; the model still validates (`em validate` in CI —
|
|
124
192
|
you didn't touch the `.em`, so this only fails if something else broke).
|
|
125
|
-
- Nothing between the slice doc and the code was committed as a source of truth (see §
|
|
193
|
+
- Nothing between the slice doc and the code was committed as a source of truth (see §9 —
|
|
126
194
|
work containers are ephemeral; generated or symlinked specs are renderings).
|
|
127
195
|
|
|
128
196
|
## 6. At merge: the lifecycle flip
|
|
129
197
|
|
|
198
|
+
The PR being merged here is the one doc's PR (§5) — its description cites exactly this slice doc
|
|
199
|
+
(plus any `covers:` keys) and nothing else, which is what makes the flip below unambiguous: one
|
|
200
|
+
PR, one `implementedIn` URL, applied to every key that PR's doc covers.
|
|
201
|
+
|
|
130
202
|
When the PR merges, exactly **two** frontmatter fields change on the slice doc:
|
|
131
203
|
|
|
132
204
|
```yaml
|
|
@@ -165,7 +237,11 @@ parked in the document as open items, exactly like a slice doc's Open Questions:
|
|
|
165
237
|
1. **Stack and architectural shape** — languages/runtimes, frameworks, the architectural pattern,
|
|
166
238
|
where a slice's code lives, and the routing table: which implementation skill or approach each
|
|
167
239
|
of the four slice patterns uses (for example, State Change slices → the `axon-*` skill set).
|
|
168
|
-
That table is what you obey in §2 when you read a slice's `pattern:`.
|
|
240
|
+
That table is what you obey in §2 when you read a slice's `pattern:`. This is also where the
|
|
241
|
+
**interface conventions** question gets asked — resource style, error/rejection mapping,
|
|
242
|
+
versioning, auth at the edge — so the shape of every endpoint the interface obligations
|
|
243
|
+
paragraph (§2) and the foundation step (§8) require is decided once, here, instead of
|
|
244
|
+
invented per slice.
|
|
169
245
|
2. **Code style** — formatter/linter, naming, module layout, and what "idiomatic" means here.
|
|
170
246
|
3. **Testing norms** — which test levels each pattern requires, where invariant and scenario
|
|
171
247
|
tests live, and the test-naming convention that lets `em coverage` trace `INV-<MNEMONIC>-n`
|
|
@@ -197,7 +273,76 @@ spec-kit's own placeholders is the team's call, and em's sections go in alongsid
|
|
|
197
273
|
`em status` will then report `constitution: present` for the model. That's the whole mechanical
|
|
198
274
|
check — em stores and locates this document, and never validates a word of it.
|
|
199
275
|
|
|
200
|
-
## 8.
|
|
276
|
+
## 8. Foundation: contracts first
|
|
277
|
+
|
|
278
|
+
Once per project, before the first slice PR ever opens, land one **foundation PR** that turns
|
|
279
|
+
the model's events — and the cross-cutting shell the constitution names — into code, and
|
|
280
|
+
nothing else. Skip this and the failure is the same on every project that's skipped it: the
|
|
281
|
+
first slice PR ships fine, then the second one needs an event the first slice already defined
|
|
282
|
+
informally, and every PR after that either redefines it slightly differently or reaches into a
|
|
283
|
+
sibling slice's package to reuse it — the boundary the constitution drew is gone within a week.
|
|
284
|
+
|
|
285
|
+
1. **What it contains.** Every event `em export <model>.em` lists in `events[]`, each placed in
|
|
286
|
+
the home the constitution's "where a slice's code lives" rule assigns to its *emitting*
|
|
287
|
+
slice — not a separate shared "events" module, so nothing about where an event lives depends
|
|
288
|
+
on this PR existing rather than the constitution. Fields come verbatim from the slice docs'
|
|
289
|
+
Event tables, with types mapped per the constitution's stack conventions. Add the
|
|
290
|
+
cross-cutting shell the constitution names: shared ports, and the request/response boundary
|
|
291
|
+
and error mapping the interface obligations above (§2) plug their endpoints into. **No
|
|
292
|
+
handlers, no projections — contracts only.** Because: a handler or a projection belongs to
|
|
293
|
+
the slice that owns the behavior behind it; folding one into the foundation PR turns a shared
|
|
294
|
+
contract into a pre-empted slice.
|
|
295
|
+
2. **It merges before any slice PR opens.** Every slice branch is cut from `main` *after* the
|
|
296
|
+
foundation PR has landed there — never from a commit that predates it, and never alongside an
|
|
297
|
+
open, unmerged foundation PR. A slice PR never carries foundation files. Because: a slice PR
|
|
298
|
+
opened while the foundation is still in review shows a diff dominated by files the slice
|
|
299
|
+
didn't write and can't be held accountable for, and its merge depends on someone else's
|
|
300
|
+
review timeline instead of its own.
|
|
301
|
+
3. **A slice never creates, edits, or reaches into another slice's code.** It *imports* events —
|
|
302
|
+
only events — from wherever the foundation placed them; a sibling slice's handlers, storage,
|
|
303
|
+
and other internals stay off limits. Because: the boundary the constitution drew only holds if
|
|
304
|
+
every slice actually stays inside it — a slice that quietly builds on a sibling's internals has
|
|
305
|
+
erased that boundary informally, one import at a time.
|
|
306
|
+
4. **A ratified delta that adds or changes an event re-runs this step for that event, first.**
|
|
307
|
+
Land the code change for the new or changed event — in its emitting slice's home, following
|
|
308
|
+
rule 1 — before the slice's delta PR opens, exactly as if it were the first pass. Because: the
|
|
309
|
+
same contract-before-consumer ordering that applies once per project applies again every time
|
|
310
|
+
the contract itself changes; an event isn't a stable shared type just because it was one
|
|
311
|
+
yesterday.
|
|
312
|
+
|
|
313
|
+
### Order of work
|
|
314
|
+
|
|
315
|
+
The foundation PR above answers most of "which slice next" by itself — once every event exists
|
|
316
|
+
as a contract, a read slice no longer has to wait for the write slice that will eventually
|
|
317
|
+
produce it. What's left is ordering the slice PRs against each other, and that ordering comes
|
|
318
|
+
from the dependency graph, not from where a slice sits on the timeline:
|
|
319
|
+
|
|
320
|
+
1. **Foundation first removes most ordering constraints.** With every event already landed as a
|
|
321
|
+
contract (rules 1–4 above), a slice's remaining dependency is on other *slices*, not on
|
|
322
|
+
events existing at all — that's what turns "order every slice against every other slice"
|
|
323
|
+
into the short, per-pattern list below.
|
|
324
|
+
2. **Then take dependency order from the graph, not the timeline.** Before starting a slice,
|
|
325
|
+
run `em query upstream <model>.em --of <slice's command-or-view ref>` — anything it returns
|
|
326
|
+
that's still `ready-to-implement` is a candidate to build first; `em query producers
|
|
327
|
+
<model>.em --event <name>` names the slices that own a specific event's producing commands
|
|
328
|
+
when you need to check one directly. Concretely, per pattern:
|
|
329
|
+
- **State Change** with no upstream is always startable — nothing but the foundation gates it.
|
|
330
|
+
- **State View** is startable as soon as the events it reads exist as contracts (the
|
|
331
|
+
foundation alone satisfies this), but build it *after* their producing slices have merged
|
|
332
|
+
when you can — its fixture tests then exercise real commands instead of hand-rolled event
|
|
333
|
+
fixtures.
|
|
334
|
+
- **Automation** goes after both the command it triggers and the to-do-list view it reads
|
|
335
|
+
(bound via `covers:` when the two are written as one doc) — a reaction can't be tested
|
|
336
|
+
against a command handler or a to-do list that doesn't exist yet.
|
|
337
|
+
- **Translation** goes after the command it triggers, same reasoning as an Automation's
|
|
338
|
+
triggered-command dependency.
|
|
339
|
+
3. **Timeline order is the ratification order, not the build order.** Ratifying slices in
|
|
340
|
+
timeline order is deliberate — it's the order stakeholders reason about the business process
|
|
341
|
+
in. Carrying that same order into implementation is the mistake this section exists to head
|
|
342
|
+
off: build order comes from the dependency graph above, and the two orders routinely
|
|
343
|
+
disagree.
|
|
344
|
+
|
|
345
|
+
## 9. Spec-kit projects: the SDD adapter
|
|
201
346
|
|
|
202
347
|
If the repository uses spec-kit (a `.specify/` directory exists), do not hand-author spec-kit
|
|
203
348
|
artifacts — allocate through **em-sdd-bridge**, redirect mode preferred:
|
|
@@ -229,7 +374,7 @@ plan/tasks as FEATURE_SPEC. The rules that keep redirect mode safe:
|
|
|
229
374
|
No spec-kit (or any SDD tool)? Implement straight from the slice doc — it already contains
|
|
230
375
|
everything a spec holds. Don't introduce an intermediate spec document of your own.
|
|
231
376
|
|
|
232
|
-
##
|
|
377
|
+
## 10. Never do
|
|
233
378
|
|
|
234
379
|
| Rule | Because |
|
|
235
380
|
|---|---|
|
|
@@ -241,8 +386,10 @@ everything a spec holds. Don't introduce an intermediate spec document of your o
|
|
|
241
386
|
| Never invent the house rules a first slice needs (stack, style, testing, NFR defaults) | They're the project's decision, ratified in the constitution — §7, not your judgment call |
|
|
242
387
|
| Never regenerate merged code from the model | Generated-then-owned: post-merge code belongs to its owners |
|
|
243
388
|
| Never commit an authored intermediate spec | The slice is the spec; anything between it and the code is a rendering |
|
|
389
|
+
| Never open a slice PR before the foundation PR has merged | The foundation is what makes an event a stable shared contract — a slice built ahead of it is building on a moving target (§8) |
|
|
390
|
+
| Never implement more than one slice doc in a PR | The PR is the durable record of one spec's implementation; `mark-implemented`, coverage, and conformance all key on one doc ↔ one `implementedIn` |
|
|
244
391
|
|
|
245
|
-
##
|
|
392
|
+
## 11. Afterward: the loop closes
|
|
246
393
|
|
|
247
394
|
Implementation isn't the end of the slice's story. On a cadence — or whenever someone asks —
|
|
248
395
|
the `conform` phase checks implemented slices against the code, using the `implementedIn` link
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling-review
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.13.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use when starting em's live browser viewer (`em watch --serve`) so a team can watch a model
|
|
6
6
|
update in real time, or facilitating a SCHEDULED stakeholder walkthrough that steps through
|
|
@@ -39,6 +39,10 @@ screen. Use Prev/Next (or the left/right arrow keys) to step through slices in d
|
|
|
39
39
|
order — each one pans/zooms into view with everything else dimmed, so the room's attention
|
|
40
40
|
tracks one slice at a time.
|
|
41
41
|
|
|
42
|
+
**When a State View slice is on screen, ask who reads it (MIL-215):** a person or client in the
|
|
43
|
+
room → the `ui` should draw it; another system → `public`; only an automation → nothing, it's
|
|
44
|
+
internal by design and needs no endpoint.
|
|
45
|
+
|
|
42
46
|
**Live capture:** when a stakeholder raises something unresolved, add `issue "..."` to the
|
|
43
47
|
relevant element in the `.em` file and save — exactly the same mechanism `event-modeling-conform`
|
|
44
48
|
uses for any other issue. The browser updates over the existing SSE push within moments, without
|