@milehimikey/em 1.10.0 → 1.12.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 +1 -1
- package/.claude/skills/event-modeling-conform/SKILL.md +5 -1
- package/.claude/skills/event-modeling-design/SKILL.md +41 -14
- package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
- package/.claude/skills/event-modeling-implement/SKILL.md +24 -7
- package/.claude/skills/event-modeling-implement/reference/implement.md +227 -19
- package/.claude/skills/event-modeling-review/SKILL.md +14 -3
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +80 -15
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +43 -1
- package/.claude/skills/event-modeling-shared/templates/constitution.md +118 -0
- package/.claude/skills/event-modeling-shared/templates/model-readme.md +2 -2
- package/.claude/skills/event-modeling-shared/templates/slice.md +4 -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 +19 -2
- package/dist/catalog/docJoin.js +30 -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/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 +8 -0
- package/dist/catalog/sliceDoc.js +2 -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 +3 -0
- package/dist/cli/ciInit.js.map +1 -1
- package/dist/cli/conformScope.js +10 -3
- package/dist/cli/conformScope.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/ledgerCheck.d.ts +72 -0
- package/dist/cli/ledgerCheck.js +122 -1
- package/dist/cli/ledgerCheck.js.map +1 -1
- package/dist/cli/markImplemented.js +12 -1
- package/dist/cli/markImplemented.js.map +1 -1
- package/dist/cli/ratify.d.ts +37 -5
- package/dist/cli/ratify.js +123 -10
- package/dist/cli/ratify.js.map +1 -1
- package/dist/cli/reratify.js +26 -6
- package/dist/cli/reratify.js.map +1 -1
- package/dist/cli/review.d.ts +38 -0
- package/dist/cli/review.js +171 -0
- package/dist/cli/review.js.map +1 -0
- package/dist/cli/sliceIndex.d.ts +3 -0
- package/dist/cli/sliceIndex.js +14 -8
- 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/stateFile.d.ts +10 -0
- package/dist/cli/stateFile.js +14 -0
- package/dist/cli/stateFile.js.map +1 -1
- package/dist/cli/status.d.ts +55 -3
- package/dist/cli/status.js +92 -19
- package/dist/cli/status.js.map +1 -1
- package/dist/cli.js +203 -47
- 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 +5 -1
- package/dist/emit/freshnessJson.js.map +1 -1
- package/dist/emit/json.d.ts +45 -1
- package/dist/emit/json.js +82 -3
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/ledgerJson.d.ts +1 -1
- package/dist/emit/ledgerJson.js +6 -1
- package/dist/emit/ledgerJson.js.map +1 -1
- package/dist/emit/queryJson.d.ts +1 -1
- package/dist/emit/queryJson.js +5 -1
- package/dist/emit/queryJson.js.map +1 -1
- package/dist/emit/statusJson.d.ts +1 -1
- package/dist/emit/statusJson.js +10 -1
- package/dist/emit/statusJson.js.map +1 -1
- package/dist/emit/validateJson.d.ts +4 -3
- package/dist/emit/validateJson.js +10 -3
- package/dist/emit/validateJson.js.map +1 -1
- package/dist/mcp/server.js +47 -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/edges.d.ts +18 -3
- package/dist/model/edges.js +34 -0
- package/dist/model/edges.js.map +1 -1
- package/dist/model/model.d.ts +4 -0
- package/dist/model/model.js +1 -0
- package/dist/model/model.js.map +1 -1
- package/dist/model/queryIndex.d.ts +5 -2
- package/dist/model/queryIndex.js +5 -2
- package/dist/model/queryIndex.js.map +1 -1
- package/dist/model/rules.d.ts +25 -0
- package/dist/model/rules.js +25 -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.js +82 -1
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +21 -0
- package/dist/parser/parser.js +96 -32
- package/dist/parser/parser.js.map +1 -1
- package/dist/render/drawEdges.js +7 -0
- package/dist/render/drawEdges.js.map +1 -1
- package/dist/render/drawNotes.d.ts +13 -3
- package/dist/render/drawNotes.js +96 -20
- package/dist/render/drawNotes.js.map +1 -1
- package/dist/render/render.js +6 -5
- package/dist/render/render.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 +14 -1
- package/dist/templates.js +141 -5
- 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/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.12.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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling-conform
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.12.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
|
|
@@ -37,6 +37,10 @@ canonical model's names wherever the code matches them); run `em diff <model-nam
|
|
|
37
37
|
proposals the user ratifies, then re-render and validate; run `em state set-conformance
|
|
38
38
|
<revision> --report <path>` to update the state file's `Last conformance:` marker.
|
|
39
39
|
|
|
40
|
+
Ruling on a finding is a human gate, like ratifying a slice
|
|
41
|
+
(docs/process.md#the-slice-lifecycle-gates): the sweep gathers evidence and proposes, a person
|
|
42
|
+
decides, and `em state set-conformance` records the decision — never run it on your own judgment.
|
|
43
|
+
|
|
40
44
|
End of phase: state file's `Last conformance:` marker updated (via `em state set-conformance`),
|
|
41
45
|
Decisions log entry if any
|
|
42
46
|
proposals were applied. Conform doesn't chain to another phase — it's a recurring loop, run
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: event-modeling-design
|
|
3
|
-
em-version: 1.
|
|
3
|
+
em-version: 1.12.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
|
|
@@ -36,9 +36,22 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
|
|
|
36
36
|
directly**. Add `translation` slices for external inputs (externally triggered: external →
|
|
37
37
|
translation → command → event, no slice before) and for the system reacting to its own state
|
|
38
38
|
(internally triggered: read model in the slice before → translation → command → event).
|
|
39
|
-
2. **Elaborate scenarios — first pass (step 6).**
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
2. **Elaborate scenarios — first pass (step 6).** Scaffold every slice's doc now, one per
|
|
40
|
+
slice, none skipped: `em slice new "<slice name>" --pattern <state-change|state-view|
|
|
41
|
+
automation|translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes
|
|
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). 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
|
|
48
|
+
its `## Scenarios (Given / When / Then)` section and the obvious invariants known so far into
|
|
49
|
+
its `## Invariants / Business Rules` section — **into the doc, never into the state file.**
|
|
50
|
+
Keep this pass shallow (no field tables, alternate/error flows, or NFRs yet — that's the full
|
|
51
|
+
deep-dive in the `slice` phase below); the docs are the home for everything collected from
|
|
52
|
+
here on, not yet implementation-ready specs. Because every doc already exists by the time the
|
|
53
|
+
`slice` phase begins, its own step 2 scaffolding call is then a no-op — that phase picks up
|
|
54
|
+
at its step 1's deeper pass.
|
|
42
55
|
3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
|
|
43
56
|
half-slice — **every command has something that triggers it**, every command emits an event,
|
|
44
57
|
**every event is read by a read model**, **every read model has a consumer**, every view has a
|
|
@@ -51,13 +64,19 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
|
|
|
51
64
|
this fact and what they do with it. The honest answers are "here's the read model we missed" or
|
|
52
65
|
"nobody — so why are we recording it", and both improve the model.
|
|
53
66
|
|
|
54
|
-
End of phase: render, update state (`em state set-phase slice`
|
|
55
|
-
|
|
67
|
+
End of phase: render, update state (`em state set-phase slice` — the mechanical marker the state
|
|
68
|
+
machine expects), and stop. Every slice already has a draft doc ready to receive the deep spec,
|
|
69
|
+
but slicing is deliberate work the team starts when it's ready for it, not this phase's automatic
|
|
70
|
+
next step.
|
|
56
71
|
|
|
57
72
|
## Phase: `slice` — deep slice documents
|
|
58
73
|
|
|
59
|
-
Goal: implementation-ready specs. Go slice by slice
|
|
60
|
-
|
|
74
|
+
Goal: implementation-ready specs. Go slice by slice **in timeline order, starting with the first
|
|
75
|
+
slice on the storyboard** — never ask the user which slice to spec first, and never propose
|
|
76
|
+
starting with whichever slice looks highest-risk or most uncertain instead. Ratification
|
|
77
|
+
proceeds in the same order: later slices' scenarios and invariants depend on earlier slices'
|
|
78
|
+
events already being settled, so working out of order means re-litigating an earlier slice's
|
|
79
|
+
contract mid-spec. Check `README.md`'s Slices table for what's already done (run `em slice index
|
|
61
80
|
<model-name>.em` first if it looks stale).
|
|
62
81
|
|
|
63
82
|
This is also where **branch / unhappy-path events** are discovered and added to the model — as a
|
|
@@ -99,6 +118,9 @@ For each slice:
|
|
|
99
118
|
"slices/<slice-name>.md"` line straight into the `.em`, onto the slice's primary element (the
|
|
100
119
|
command for State Change, the view for State View, the processor for Automation, the
|
|
101
120
|
translation for Translation), matched by export key — see step 4 for when it refuses instead.
|
|
121
|
+
A slice whose sole candidate view is a later `view X again` instance is a **continuation**
|
|
122
|
+
(MIL-208): `--wire` refuses it and names the originating slice holding `X`'s first
|
|
123
|
+
declaration — write this slice's scenarios into that doc instead of scaffolding a new one.
|
|
102
124
|
Both `--pattern` and `--swimlane` are required; an invalid `--pattern` is refused with the
|
|
103
125
|
valid choices listed. Never hand-type this block, and never fall back to a placeholder
|
|
104
126
|
pattern/swimlane to dodge the flags. Then fill in every judgment section below the stub from
|
|
@@ -130,10 +152,15 @@ For each slice:
|
|
|
130
152
|
`implementedIn` once shipped). Never hand-edit the table; it's a generated block.
|
|
131
153
|
6. Re-render and `em validate`.
|
|
132
154
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
`
|
|
136
|
-
at the `event-modeling-implement` skill.
|
|
155
|
+
A finished draft doesn't become implementable here: it goes through the review gate and then the
|
|
156
|
+
ratification gate, both human, both outside this phase — see
|
|
157
|
+
`docs/process.md#the-slice-lifecycle-gates` for who runs which and in what order.
|
|
137
158
|
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
Before the project's first slice is implemented it also needs a ratified implementation
|
|
160
|
+
constitution (stack, code style, testing norms, NFR baselines, review norms) — the
|
|
161
|
+
`event-modeling-implement` skill elicits it, see its `reference/implement.md` §7.
|
|
162
|
+
|
|
163
|
+
End of phase: every slice that should exist has a doc — except a continuation slice (MIL-208, a
|
|
164
|
+
later `view X again` instance), which has none by design and is documented by the originating
|
|
165
|
+
slice instead — `README.md`'s Slices table is current, and the model validates clean. Suggest
|
|
166
|
+
`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.12.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.12.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
|
|
@@ -15,11 +15,27 @@ the agent builds rather than facilitates.
|
|
|
15
15
|
|
|
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
|
-
back if it fails — never edit the doc to make the gate pass);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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; 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`.
|
|
30
|
+
|
|
31
|
+
**Gate: no constitution, no first slice.** The constitution is the project's ratified house rules
|
|
32
|
+
— stack and architectural shape (including which implementation skill each slice pattern routes
|
|
33
|
+
to), code style, testing norms, NFR baselines, review/merge norms. It lives at
|
|
34
|
+
`.specify/memory/constitution.md` in a spec-kit project, otherwise `constitution.md` beside the
|
|
35
|
+
model; `em status` reports which, and whether it's there. If it's missing — or its `ratifiedBy:`
|
|
36
|
+
is empty, meaning nobody has signed it off — run `reference/implement.md` §7 ("Before the first
|
|
37
|
+
slice: the constitution") first: ask its questions conversationally, write the answers in, and
|
|
38
|
+
have a human ratify them. Don't decide the project's stack or style yourself to get moving.
|
|
23
39
|
|
|
24
40
|
That contract applies to any implementing agent, whether or not the session started from
|
|
25
41
|
`/event-modeling` — `em contract` prints it to stdout with no `.claude/` awareness required, for
|
|
@@ -27,7 +43,8 @@ an agent that can run a shell but isn't Claude Code.
|
|
|
27
43
|
|
|
28
44
|
If the project ships pattern-specific implementation skills (for example an Axon/DCB skill set
|
|
29
45
|
keyed on a slice doc's `pattern:` frontmatter), route by the slice's pattern and follow those —
|
|
30
|
-
`reference/implement.md` §2 explains where that hand-off happens
|
|
46
|
+
`reference/implement.md` §2 explains where that hand-off happens, and the constitution's
|
|
47
|
+
routing table (§7) is what names the skill set for each pattern.
|
|
31
48
|
|
|
32
49
|
Preconditions (tool check, model location) are the same as every other phase — see
|
|
33
50
|
`../event-modeling-shared/reference/operating-principles.md` — but this phase additionally
|
|
@@ -26,22 +26,41 @@ 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
|
-
|
|
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` also reads `ready: false` (its `statusReady` gate fails because
|
|
39
|
+
nothing is newly ratified), and that specific case is re-implementation/repair, not a blocker —
|
|
40
|
+
see §3's third mode before treating it as one. For every other `false`, report which `gates`
|
|
41
|
+
entries are `false` (and any `diagnostics` entries concerning this slice) and hand the slice
|
|
42
|
+
back to the humans. Never make the gate pass yourself — checking an open-question box, flipping
|
|
43
|
+
`status`, or editing frontmatter are ratification decisions, and ratification happens in a
|
|
44
|
+
facilitated session, not in an implementation branch.
|
|
41
45
|
|
|
42
46
|
## 2. Read the spec
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
**First, read the project's constitution** — the house rules for *how* implementation happens
|
|
49
|
+
here (stack and architectural shape, which implementation skill each slice pattern routes to,
|
|
50
|
+
code style, testing norms, NFR baselines, review and merge norms). It lives in one of exactly two
|
|
51
|
+
places, never both:
|
|
52
|
+
|
|
53
|
+
- **spec-kit project** (a `.specify/` directory exists at or above the model):
|
|
54
|
+
`.specify/memory/constitution.md` — spec-kit's own slot, which em defers to.
|
|
55
|
+
- **anything else**: `constitution.md` beside the model, where `em scaffold` writes it.
|
|
56
|
+
|
|
57
|
+
`em status <model>.em --json` reports which one applies and whether it's there
|
|
58
|
+
(`conformance[].constitution`) — existence only; nothing in `em` reads or judges its content.
|
|
59
|
+
**Its rules bound every technical choice you make on this slice** (see §4). If it's missing, or
|
|
60
|
+
its `ratifiedBy:` is empty (spec-kit's file: the `**Ratified**:` footer still a placeholder),
|
|
61
|
+
it isn't ratified — go to §7 before writing code.
|
|
62
|
+
|
|
63
|
+
Then read `slices/<slice-key>.md` end to end — every section is load-bearing:
|
|
45
64
|
|
|
46
65
|
| Section | What it is to the implementer |
|
|
47
66
|
|---|---|
|
|
@@ -53,18 +72,42 @@ Read `slices/<slice-key>.md` end to end — every section is load-bearing:
|
|
|
53
72
|
| `## Non-Functional Requirements` | Authz, PII, performance — implement or consciously surface, never skip |
|
|
54
73
|
| `## Dependencies & Read Models Affected` | The blast radius — what else to check before and after |
|
|
55
74
|
|
|
75
|
+
**Interface obligations follow from the model, not from the doc's prose.** The DSL already
|
|
76
|
+
encodes what's reachable — see `em-dsl.md`'s "Headless / API systems & repeated read models"
|
|
77
|
+
section — and the routing from it is mechanical, not a technical choice you make while coding:
|
|
78
|
+
|
|
79
|
+
- A **command with a `ui` trigger** ships the endpoint that dispatches it as part of this slice
|
|
80
|
+
— shape, verb, status codes, and error mapping follow the constitution's interface conventions
|
|
81
|
+
(§7). Because: the model already said this command is reachable from outside; leaving it
|
|
82
|
+
reachable only through an internal gateway silently narrows what the model ratified.
|
|
83
|
+
- A **view with a `ui` consumer** ships the query endpoint as part of this slice, same
|
|
84
|
+
conventions.
|
|
85
|
+
- **No `ui`** on the trigger or the consumer (a command dispatched by a processor or a
|
|
86
|
+
translation, a view read only by a reaction) means internal-only: no endpoint. If one seems
|
|
87
|
+
needed anyway, that's a gap to surface (§4), not a technical choice to make mid-implementation
|
|
88
|
+
— adding a route the model doesn't show is exactly the kind of silent divergence §4 exists to
|
|
89
|
+
catch.
|
|
90
|
+
- **`public` events and views** are the model's published integration surface: their shape is a
|
|
91
|
+
contract, not an implementation detail. `em validate --list-public` lists them; `em typespec`
|
|
92
|
+
is the POC generator for a downstream schema. Changing a public element's shape is a ratified
|
|
93
|
+
delta, never an implementation-time choice.
|
|
94
|
+
|
|
56
95
|
For timeline context, read the slice's surroundings in the `.em` (what triggers it, what
|
|
57
96
|
consumes its output) — the slice's own diagram (`slices/<slice-key>.svg`) shows its canonical
|
|
58
97
|
pattern shape. For machine-readable facts, use `em export <model>.em --slice <slice-key>`
|
|
59
98
|
rather than parsing the DSL or the doc's frontmatter yourself: it returns just this slice's
|
|
60
99
|
object — `pattern`, element refs, fields, and the joined doc metadata (`slice.doc.status`,
|
|
61
100
|
`version`, `driftSignal`) — without piping the whole model's export to find one `slice.doc`.
|
|
101
|
+
When the view repeats later via `again` (MIL-208), that same call on the *originating* slice
|
|
102
|
+
also returns `alsoReads: [{ event, atSlice }]` — the union of every event the read model
|
|
103
|
+
consumes across all its later continuations, in timeline order — so implementing it never means
|
|
104
|
+
walking each continuation by hand to assemble the full event list.
|
|
62
105
|
|
|
63
106
|
If the project ships pattern-specific implementation skills (for example an Axon/DCB skill
|
|
64
107
|
set keyed on a slice doc's `pattern:` frontmatter), route by the slice's pattern and follow
|
|
65
108
|
those.
|
|
66
109
|
|
|
67
|
-
## 3.
|
|
110
|
+
## 3. Three modes: first version, ratified delta, re-implementation/repair
|
|
68
111
|
|
|
69
112
|
Check the doc's `version:` and `## Delta` section (or `em export`'s `slice.doc.driftSignal`):
|
|
70
113
|
|
|
@@ -78,6 +121,26 @@ Check the doc's `version:` and `## Delta` section (or `em export`'s `slice.doc.d
|
|
|
78
121
|
**Never regenerate merged code wholesale from the model** — after merge, code is owned by
|
|
79
122
|
the people who've edited it (generated-then-owned); regeneration is legal only for slices
|
|
80
123
|
whose code never merged.
|
|
124
|
+
- **Re-implementation / repair** (`status: implemented`, `implementedIn` set, `driftSignal:
|
|
125
|
+
in-sync` — `--slice-ready` reports `ready: false` purely because `statusReady` fails, with no
|
|
126
|
+
other gate false and no doc content changed): the doc hasn't changed, but the shipped code
|
|
127
|
+
needs rebuilding or fixing — deleted code, a reverted PR, a bug in behavior the doc already
|
|
128
|
+
specifies. **The doc is still the spec.** The gate's `false` here means "nothing new is
|
|
129
|
+
ratified," not "stop" — it is not license to self-initiate: proceed only when a human has
|
|
130
|
+
asked for this specific re-work. Scope the change to what the repair needs, exactly like a
|
|
131
|
+
delta (generated-then-owned still applies — don't regenerate the whole slice wholesale just
|
|
132
|
+
because the gate reads oddly); if what you'd change is really a change in behavior rather than
|
|
133
|
+
a repair of it, that's a ratified delta (the bullet above), not this mode — when unsure which
|
|
134
|
+
one you're in, ask the human rather than guess.
|
|
135
|
+
|
|
136
|
+
No lifecycle flip is needed unless `implementedIn` changes. If it does,
|
|
137
|
+
`em slice mark-implemented <model>.em <slice-key> <new-pr-url>` will **refuse** rather than
|
|
138
|
+
overwrite: a doc already `implemented` with a different `implementedIn` URL is left untouched
|
|
139
|
+
and the command reports the conflict (`src/cli/markImplemented.ts`) instead of silently
|
|
140
|
+
replacing provenance. That refusal is correct, not a bug to route around — never hand-edit the
|
|
141
|
+
frontmatter to force it through. Surface the conflict to the human instead: they decide whether
|
|
142
|
+
the new PR supersedes the recorded one (and re-run `mark-implemented` themselves, or ask you to,
|
|
143
|
+
once they've decided) or whether the existing link should stand.
|
|
81
144
|
|
|
82
145
|
## 4. Gaps: propose, never decide
|
|
83
146
|
|
|
@@ -92,10 +155,27 @@ undocumented ordering, a contradiction with adjacent code. The discipline:
|
|
|
92
155
|
a business person could have an opinion on. Silent divergence is the failure mode the whole
|
|
93
156
|
conformance loop exists to catch — don't manufacture it.
|
|
94
157
|
- Purely technical choices with no behavioral surface (private naming, idiomatic structure,
|
|
95
|
-
which assertion library) are yours
|
|
158
|
+
which assertion library) are yours — **within the constitution's rules** (§2); anything
|
|
159
|
+
observable in behavior, data, or contract is not. If no constitution exists (or its
|
|
160
|
+
`ratifiedBy` is empty), surface that to the human and do not proceed on unilateral style or
|
|
161
|
+
stack decisions for a first slice: run the constitution step (§7) first.
|
|
96
162
|
|
|
97
163
|
## 5. Definition of done
|
|
98
164
|
|
|
165
|
+
**The unit is the slice doc: one slice doc = one PR.** A doc that `covers:` another slice (an
|
|
166
|
+
automation's to-do-list view and its reaction, MIL-121) is one spec, not two — it ships in one
|
|
167
|
+
PR, and `mark-implemented` runs for both keys against the same URL. A read model is one doc,
|
|
168
|
+
even when a `view X again` clause repeats it across later positions on the timeline (MIL-208):
|
|
169
|
+
the later instance is a **continuation** of the slice holding `X`'s first declaration, not a
|
|
170
|
+
spec unit of its own — no doc, no status, no ratification, no PR of its own. Its behavior
|
|
171
|
+
belongs in the originating doc's `## Scenarios` (one scenario per event the later instance
|
|
172
|
+
feeds), not a doc of its own; `em export --slice <originating-key>` lists the full union of
|
|
173
|
+
events the read model consumes across every position via `alsoReads` (§2). Never fold multiple
|
|
174
|
+
slice docs' implementations into one PR because they touch the same read model, ship together,
|
|
175
|
+
or seem small — see §10.
|
|
176
|
+
|
|
177
|
+
- The PR description cites exactly one slice doc (plus its `covers:` keys, if any) and lists
|
|
178
|
+
that doc's invariants alongside the tests that cover them.
|
|
99
179
|
- Every `INV-<MNEMONIC>-n` invariant has at least one test that cites its ID — checked mechanically by
|
|
100
180
|
`em coverage <model>.em --tests <dir>` (MIL-130); run it (add `--strict` in CI) rather than
|
|
101
181
|
eyeballing citations by hand.
|
|
@@ -104,11 +184,15 @@ undocumented ordering, a contradiction with adjacent code. The discipline:
|
|
|
104
184
|
- Alternate/error flows (idempotency included) are covered by tests.
|
|
105
185
|
- The build and full test suite are green; the model still validates (`em validate` in CI —
|
|
106
186
|
you didn't touch the `.em`, so this only fails if something else broke).
|
|
107
|
-
- Nothing between the slice doc and the code was committed as a source of truth (see §
|
|
187
|
+
- Nothing between the slice doc and the code was committed as a source of truth (see §9 —
|
|
108
188
|
work containers are ephemeral; generated or symlinked specs are renderings).
|
|
109
189
|
|
|
110
190
|
## 6. At merge: the lifecycle flip
|
|
111
191
|
|
|
192
|
+
The PR being merged here is the one doc's PR (§5) — its description cites exactly this slice doc
|
|
193
|
+
(plus any `covers:` keys) and nothing else, which is what makes the flip below unambiguous: one
|
|
194
|
+
PR, one `implementedIn` URL, applied to every key that PR's doc covers.
|
|
195
|
+
|
|
112
196
|
When the PR merges, exactly **two** frontmatter fields change on the slice doc:
|
|
113
197
|
|
|
114
198
|
```yaml
|
|
@@ -131,7 +215,128 @@ Then, if the project keeps a model README (from `../../event-modeling-shared/tem
|
|
|
131
215
|
link — never hand-edit that table. The `implementedIn` link is what the `conform` phase later
|
|
132
216
|
uses to anchor drift-checking — leaving it empty blinds the loop.
|
|
133
217
|
|
|
134
|
-
## 7.
|
|
218
|
+
## 7. Before the first slice: the constitution
|
|
219
|
+
|
|
220
|
+
The first time this project's slices reach implementation — or any time §2 finds no constitution,
|
|
221
|
+
or one whose `ratifiedBy` is still empty — **stop and run this short conversation before writing
|
|
222
|
+
code.** It happens once per project, not once per slice.
|
|
223
|
+
|
|
224
|
+
Start from the template the skill bundle ships,
|
|
225
|
+
`../../event-modeling-shared/templates/constitution.md`. Each of its five sections opens with the
|
|
226
|
+
question to ask; ask them **conversationally, one section at a time**, and write the answers into
|
|
227
|
+
the document as you go — never fill a section by guessing, by reading the codebase and inferring
|
|
228
|
+
"what they probably do", or by pasting the template's own examples. Unanswerable questions get
|
|
229
|
+
parked in the document as open items, exactly like a slice doc's Open Questions:
|
|
230
|
+
|
|
231
|
+
1. **Stack and architectural shape** — languages/runtimes, frameworks, the architectural pattern,
|
|
232
|
+
where a slice's code lives, and the routing table: which implementation skill or approach each
|
|
233
|
+
of the four slice patterns uses (for example, State Change slices → the `axon-*` skill set).
|
|
234
|
+
That table is what you obey in §2 when you read a slice's `pattern:`. This is also where the
|
|
235
|
+
**interface conventions** question gets asked — resource style, error/rejection mapping,
|
|
236
|
+
versioning, auth at the edge — so the shape of every endpoint the interface obligations
|
|
237
|
+
paragraph (§2) and the foundation step (§8) require is decided once, here, instead of
|
|
238
|
+
invented per slice.
|
|
239
|
+
2. **Code style** — formatter/linter, naming, module layout, and what "idiomatic" means here.
|
|
240
|
+
3. **Testing norms** — which test levels each pattern requires, where invariant and scenario
|
|
241
|
+
tests live, and the test-naming convention that lets `em coverage` trace `INV-<MNEMONIC>-n`
|
|
242
|
+
citations.
|
|
243
|
+
4. **NFR baselines** — authz model, PII handling, performance defaults, observability: the
|
|
244
|
+
standing defaults every slice inherits where its own `## Non-Functional Requirements` section
|
|
245
|
+
is silent.
|
|
246
|
+
5. **Review and merge norms** — who reviews, what blocks a merge, and who runs
|
|
247
|
+
`em slice mark-implemented` at merge (§6).
|
|
248
|
+
|
|
249
|
+
**A human ratifies it.** The answers are the project's, not yours — you drafted the wording, they
|
|
250
|
+
sign it off. In the em-native file that's the `ratifiedBy:`/`ratifiedOn:` frontmatter (a named
|
|
251
|
+
person and an ISO date, hand-filled — no `em` command writes this file, and an empty `ratifiedBy:`
|
|
252
|
+
means *draft*). In a spec-kit project it's that file's own `**Ratified**:` footer line — never add
|
|
253
|
+
frontmatter to spec-kit's document. Until it's ratified, treat it as a proposal: you may not
|
|
254
|
+
build a first slice on it.
|
|
255
|
+
|
|
256
|
+
**In a spec-kit project, merge — never duplicate.** The constitution IS
|
|
257
|
+
`.specify/memory/constitution.md`; em writes no second file and neither do you. Append em's five
|
|
258
|
+
sections to it under their own `##` headings, **leaving every existing section byte-untouched** —
|
|
259
|
+
including spec-kit's own `## Core Principles` and `## Governance`, and any principle a team
|
|
260
|
+
already wrote. If a heading of the same name is already there, add to it rather than replacing it,
|
|
261
|
+
and show the human the diff before writing. Two cautions: spec-kit's `/speckit.constitution`
|
|
262
|
+
command rewrites that whole file from its template, so run it (if at all) **before** this
|
|
263
|
+
conversation, never after — and never use it to author em's sections. If the file is still the
|
|
264
|
+
blank stock template (`[PROJECT_NAME]`, `[PRINCIPLE_1_NAME]`, … placeholders), say so: filling
|
|
265
|
+
spec-kit's own placeholders is the team's call, and em's sections go in alongside them either way.
|
|
266
|
+
|
|
267
|
+
`em status` will then report `constitution: present` for the model. That's the whole mechanical
|
|
268
|
+
check — em stores and locates this document, and never validates a word of it.
|
|
269
|
+
|
|
270
|
+
## 8. Foundation: contracts first
|
|
271
|
+
|
|
272
|
+
Once per project, before the first slice PR ever opens, land one **foundation PR** that turns
|
|
273
|
+
the model's events — and the cross-cutting shell the constitution names — into code, and
|
|
274
|
+
nothing else. Skip this and the failure is the same on every project that's skipped it: the
|
|
275
|
+
first slice PR ships fine, then the second one needs an event the first slice already defined
|
|
276
|
+
informally, and every PR after that either redefines it slightly differently or reaches into a
|
|
277
|
+
sibling slice's package to reuse it — the boundary the constitution drew is gone within a week.
|
|
278
|
+
|
|
279
|
+
1. **What it contains.** Every event `em export <model>.em` lists in `events[]`, each placed in
|
|
280
|
+
the home the constitution's "where a slice's code lives" rule assigns to its *emitting*
|
|
281
|
+
slice — not a separate shared "events" module, so nothing about where an event lives depends
|
|
282
|
+
on this PR existing rather than the constitution. Fields come verbatim from the slice docs'
|
|
283
|
+
Event tables, with types mapped per the constitution's stack conventions. Add the
|
|
284
|
+
cross-cutting shell the constitution names: shared ports, and the request/response boundary
|
|
285
|
+
and error mapping the interface obligations above (§2) plug their endpoints into. **No
|
|
286
|
+
handlers, no projections — contracts only.** Because: a handler or a projection belongs to
|
|
287
|
+
the slice that owns the behavior behind it; folding one into the foundation PR turns a shared
|
|
288
|
+
contract into a pre-empted slice.
|
|
289
|
+
2. **It merges before any slice PR opens.** Every slice branch is cut from `main` *after* the
|
|
290
|
+
foundation PR has landed there — never from a commit that predates it, and never alongside an
|
|
291
|
+
open, unmerged foundation PR. A slice PR never carries foundation files. Because: a slice PR
|
|
292
|
+
opened while the foundation is still in review shows a diff dominated by files the slice
|
|
293
|
+
didn't write and can't be held accountable for, and its merge depends on someone else's
|
|
294
|
+
review timeline instead of its own.
|
|
295
|
+
3. **A slice never creates, edits, or reaches into another slice's code.** It *imports* events —
|
|
296
|
+
only events — from wherever the foundation placed them; a sibling slice's handlers, storage,
|
|
297
|
+
and other internals stay off limits. Because: the boundary the constitution drew only holds if
|
|
298
|
+
every slice actually stays inside it — a slice that quietly builds on a sibling's internals has
|
|
299
|
+
erased that boundary informally, one import at a time.
|
|
300
|
+
4. **A ratified delta that adds or changes an event re-runs this step for that event, first.**
|
|
301
|
+
Land the code change for the new or changed event — in its emitting slice's home, following
|
|
302
|
+
rule 1 — before the slice's delta PR opens, exactly as if it were the first pass. Because: the
|
|
303
|
+
same contract-before-consumer ordering that applies once per project applies again every time
|
|
304
|
+
the contract itself changes; an event isn't a stable shared type just because it was one
|
|
305
|
+
yesterday.
|
|
306
|
+
|
|
307
|
+
### Order of work
|
|
308
|
+
|
|
309
|
+
The foundation PR above answers most of "which slice next" by itself — once every event exists
|
|
310
|
+
as a contract, a read slice no longer has to wait for the write slice that will eventually
|
|
311
|
+
produce it. What's left is ordering the slice PRs against each other, and that ordering comes
|
|
312
|
+
from the dependency graph, not from where a slice sits on the timeline:
|
|
313
|
+
|
|
314
|
+
1. **Foundation first removes most ordering constraints.** With every event already landed as a
|
|
315
|
+
contract (rules 1–4 above), a slice's remaining dependency is on other *slices*, not on
|
|
316
|
+
events existing at all — that's what turns "order every slice against every other slice"
|
|
317
|
+
into the short, per-pattern list below.
|
|
318
|
+
2. **Then take dependency order from the graph, not the timeline.** Before starting a slice,
|
|
319
|
+
run `em query upstream <model>.em --of <slice's command-or-view ref>` — anything it returns
|
|
320
|
+
that's still `ready-to-implement` is a candidate to build first; `em query producers
|
|
321
|
+
<model>.em --event <name>` names the slices that own a specific event's producing commands
|
|
322
|
+
when you need to check one directly. Concretely, per pattern:
|
|
323
|
+
- **State Change** with no upstream is always startable — nothing but the foundation gates it.
|
|
324
|
+
- **State View** is startable as soon as the events it reads exist as contracts (the
|
|
325
|
+
foundation alone satisfies this), but build it *after* their producing slices have merged
|
|
326
|
+
when you can — its fixture tests then exercise real commands instead of hand-rolled event
|
|
327
|
+
fixtures.
|
|
328
|
+
- **Automation** goes after both the command it triggers and the to-do-list view it reads
|
|
329
|
+
(bound via `covers:` when the two are written as one doc) — a reaction can't be tested
|
|
330
|
+
against a command handler or a to-do list that doesn't exist yet.
|
|
331
|
+
- **Translation** goes after the command it triggers, same reasoning as an Automation's
|
|
332
|
+
triggered-command dependency.
|
|
333
|
+
3. **Timeline order is the ratification order, not the build order.** Ratifying slices in
|
|
334
|
+
timeline order is deliberate — it's the order stakeholders reason about the business process
|
|
335
|
+
in. Carrying that same order into implementation is the mistake this section exists to head
|
|
336
|
+
off: build order comes from the dependency graph above, and the two orders routinely
|
|
337
|
+
disagree.
|
|
338
|
+
|
|
339
|
+
## 9. Spec-kit projects: the SDD adapter
|
|
135
340
|
|
|
136
341
|
If the repository uses spec-kit (a `.specify/` directory exists), do not hand-author spec-kit
|
|
137
342
|
artifacts — allocate through **em-sdd-bridge**, redirect mode preferred:
|
|
@@ -163,7 +368,7 @@ plan/tasks as FEATURE_SPEC. The rules that keep redirect mode safe:
|
|
|
163
368
|
No spec-kit (or any SDD tool)? Implement straight from the slice doc — it already contains
|
|
164
369
|
everything a spec holds. Don't introduce an intermediate spec document of your own.
|
|
165
370
|
|
|
166
|
-
##
|
|
371
|
+
## 10. Never do
|
|
167
372
|
|
|
168
373
|
| Rule | Because |
|
|
169
374
|
|---|---|
|
|
@@ -172,10 +377,13 @@ everything a spec holds. Don't introduce an intermediate spec document of your o
|
|
|
172
377
|
| Never edit the `.em` model | Model edits are ratified decisions — propose, don't write |
|
|
173
378
|
| Never bump `version:` | Versions move only with ratified deltas; see `em ledger` |
|
|
174
379
|
| Never silently decide unspecified behavior | Silent divergence is the disease the conformance loop exists to catch |
|
|
380
|
+
| 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 |
|
|
175
381
|
| Never regenerate merged code from the model | Generated-then-owned: post-merge code belongs to its owners |
|
|
176
382
|
| Never commit an authored intermediate spec | The slice is the spec; anything between it and the code is a rendering |
|
|
383
|
+
| 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) |
|
|
384
|
+
| 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` |
|
|
177
385
|
|
|
178
|
-
##
|
|
386
|
+
## 11. Afterward: the loop closes
|
|
179
387
|
|
|
180
388
|
Implementation isn't the end of the slice's story. On a cadence — or whenever someone asks —
|
|
181
389
|
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.12.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
|
|
@@ -49,8 +49,19 @@ walk each one with the user, same as any open issue. Update the state file's Par
|
|
|
49
49
|
section with who attended, and run `em state set-review <date>` to set the `Last stakeholder
|
|
50
50
|
review:` marker (mirrors `Last conformance:`).
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
**Per-slice outcome.** A slice whose open questions are all resolved in the room →
|
|
53
|
+
`em slice review <model>.em <key> --by <name>` (flips its doc to `status: reviewed` and records
|
|
54
|
+
`reviewedBy:`/`reviewedOn:`). Anything still open stays `draft` — the walkthrough doesn't decide
|
|
55
|
+
it, the next session or the open-questions list does.
|
|
56
|
+
|
|
57
|
+
**The facilitator never ratifies, never offers to ratify, and never suggests ratification in a
|
|
58
|
+
review session. Ratification is a separate human gate after review**
|
|
59
|
+
(docs/process.md#the-slice-lifecycle-gates). It is typically multi-person, routed through
|
|
60
|
+
CODEOWNERS on `slices/**`, and it is not this session's to run — `em slice ratify` will itself
|
|
61
|
+
refuse a doc that hasn't been through `em slice review`.
|
|
62
|
+
|
|
63
|
+
End of phase: state file's `Last stakeholder review:` marker updated, every walked slice whose
|
|
64
|
+
questions all closed moved to `status: reviewed`, every issue captured live triaged (resolved on the spot, moved to Open questions / parking lot, or left open on
|
|
54
65
|
purpose). Review doesn't chain to another phase — it's a recurring, scheduled activity like
|
|
55
66
|
`conform`, not a step in the discover → model → slice sequence.
|
|
56
67
|
|