@milehimikey/em 1.4.0 → 1.6.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 +121 -30
- package/.claude/skills/event-modeling/reference/conform.md +36 -7
- package/.claude/skills/event-modeling/reference/em-dsl.md +210 -48
- package/.claude/skills/event-modeling/reference/extract.md +24 -7
- package/.claude/skills/event-modeling/reference/methodology.md +129 -32
- package/.claude/skills/event-modeling/templates/conformance-report.md +9 -7
- package/.claude/skills/event-modeling/templates/model-readme.md +9 -0
- package/.claude/skills/event-modeling/templates/slice.md +20 -1
- package/.claude/skills/event-modeling/templates/state.md +8 -0
- package/README.md +1 -0
- package/dist/catalog/build.d.ts +33 -0
- package/dist/catalog/build.js +117 -0
- package/dist/catalog/build.js.map +1 -0
- package/dist/catalog/classify.d.ts +14 -0
- package/dist/catalog/classify.js +44 -0
- package/dist/catalog/classify.js.map +1 -0
- package/dist/catalog/pages.d.ts +45 -0
- package/dist/catalog/pages.js +174 -0
- package/dist/catalog/pages.js.map +1 -0
- package/dist/catalog/sliceDoc.d.ts +10 -0
- package/dist/catalog/sliceDoc.js +20 -0
- package/dist/catalog/sliceDoc.js.map +1 -0
- package/dist/cli/catalog-inputs.d.ts +16 -0
- package/dist/cli/catalog-inputs.js +18 -0
- package/dist/cli/catalog-inputs.js.map +1 -0
- package/dist/cli/glossary-inputs.d.ts +15 -0
- package/dist/cli/glossary-inputs.js +17 -0
- package/dist/cli/glossary-inputs.js.map +1 -0
- package/dist/cli/render-inputs.d.ts +27 -0
- package/dist/cli/render-inputs.js +41 -0
- package/dist/cli/render-inputs.js.map +1 -0
- package/dist/cli.js +188 -10
- package/dist/cli.js.map +1 -1
- package/dist/emit/diffJson.d.ts +1 -1
- package/dist/emit/diffJson.js +8 -1
- package/dist/emit/diffJson.js.map +1 -1
- package/dist/emit/dot.d.ts +7 -0
- package/dist/emit/dot.js +10 -4
- package/dist/emit/dot.js.map +1 -1
- package/dist/emit/glossaryJson.d.ts +13 -0
- package/dist/emit/glossaryJson.js +36 -0
- package/dist/emit/glossaryJson.js.map +1 -0
- package/dist/emit/json.d.ts +16 -2
- package/dist/emit/json.js +57 -6
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/theme.d.ts +3 -0
- package/dist/emit/theme.js +19 -0
- package/dist/emit/theme.js.map +1 -1
- package/dist/model/diff.d.ts +36 -1
- package/dist/model/diff.js +198 -35
- package/dist/model/diff.js.map +1 -1
- package/dist/model/edges.d.ts +12 -1
- package/dist/model/edges.js +26 -24
- package/dist/model/edges.js.map +1 -1
- package/dist/model/glossary.d.ts +68 -0
- package/dist/model/glossary.js +138 -0
- package/dist/model/glossary.js.map +1 -0
- package/dist/model/model.d.ts +35 -0
- package/dist/model/model.js +41 -0
- package/dist/model/model.js.map +1 -1
- package/dist/model/validate.js +252 -1
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +20 -0
- package/dist/parser/parser.js +158 -29
- package/dist/parser/parser.js.map +1 -1
- package/dist/render/drawEdges.d.ts +3 -1
- package/dist/render/drawEdges.js +119 -7
- package/dist/render/drawEdges.js.map +1 -1
- package/dist/render/drawNotes.d.ts +7 -3
- package/dist/render/drawNotes.js +75 -28
- package/dist/render/drawNotes.js.map +1 -1
- package/dist/render/render.d.ts +20 -1
- package/dist/render/render.js +57 -17
- package/dist/render/render.js.map +1 -1
- package/dist/render/serve.js +155 -0
- package/dist/render/serve.js.map +1 -1
- package/dist/render/sliceDiagram.d.ts +11 -0
- package/dist/render/sliceDiagram.js +198 -0
- package/dist/render/sliceDiagram.js.map +1 -0
- package/dist/render/sliceOverlay.d.ts +21 -0
- package/dist/render/sliceOverlay.js +111 -0
- package/dist/render/sliceOverlay.js.map +1 -0
- package/dist/render/sliceStatus.d.ts +5 -0
- package/dist/render/sliceStatus.js +26 -0
- package/dist/render/sliceStatus.js.map +1 -0
- package/dist/render/statusOverlay.d.ts +16 -0
- package/dist/render/statusOverlay.js +110 -0
- package/dist/render/statusOverlay.js.map +1 -0
- package/dist/render/svgGeometry.d.ts +43 -0
- package/dist/render/svgGeometry.js +69 -3
- package/dist/render/svgGeometry.js.map +1 -1
- package/dist/render/watch.d.ts +1 -1
- package/dist/render/watch.js +5 -2
- package/dist/render/watch.js.map +1 -1
- package/package.json +3 -2
|
@@ -42,19 +42,46 @@ validation rules) before doing real work — they are the source of truth. Templ
|
|
|
42
42
|
and show the user what changed. Encourage running the live view (`watch`) so a team can follow.
|
|
43
43
|
- **Keep the `.em` structural; put depth in `note` docs.** The diagram holds flow; invariants,
|
|
44
44
|
fields, and scenarios live in `slices/*.md` linked via `note "slices/<name>.md"`.
|
|
45
|
-
- **
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
- **Headless/API models still use `ui`.** If the system is headless (clients call an API, not
|
|
46
|
+
screens), declare a persona per external caller/role (e.g. `IntegratorAPI`) and treat its `ui`
|
|
47
|
+
boxes as API calls, not screens: writes are `ui → command → event` and reads are
|
|
48
|
+
`read model → ui`, exactly like any other State Change/State View slice — no separate trigger
|
|
49
|
+
slice. `translation` stays reserved for genuine reactions and real external-system boundaries,
|
|
50
|
+
not a synchronous request/response call. Internal-only commands/views with no public route carry
|
|
51
|
+
no `ui` at all — they follow the ordinary Automation pattern instead. **Repeat read models**
|
|
52
|
+
across slices so the timeline flows left-to-right — put each repeat **right after the event that feeds it**, sourcing
|
|
50
53
|
only that one adjacent event, so every arrow is short (a read model far from its source events
|
|
51
|
-
draws long
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
draws a long arrow whose head lands columns away, making the *write* slice read as dangling —
|
|
55
|
+
keep a sub-flow that detours into another context together, not parked at the end of the model).
|
|
56
|
+
Declare every instance after the first with
|
|
57
|
+
**`view X again from "..."`** — `again` instances are exempt from the duplicate-name warning even
|
|
58
|
+
when referenced, and each reference resolves to the right instance. Slice order matters: a
|
|
59
|
+
reaction must be directly followed by its command slice, and a read slice must not be immediately
|
|
54
60
|
followed by a command slice (see `em-dsl.md`).
|
|
55
61
|
See `reference/methodology.md` (State View) and `reference/em-dsl.md`.
|
|
62
|
+
- **Never connect two instances of one read model.** The repeat is a timeline device, not a flow:
|
|
63
|
+
continuity is implied by the shared name, and the events arriving at each instance are what show
|
|
64
|
+
it changing. An arrow between instances says the read model feeds itself, and is an error.
|
|
65
|
+
- **Every slice is a COMPLETE pattern, never a half-slice.** A State Change is
|
|
66
|
+
`ui → command → event`; a State View is `event → read model → ui` (or `→ reaction`). So: a
|
|
67
|
+
command needs something that **triggers** it (the `ui` it's issued from, or the reaction in the
|
|
68
|
+
slice before it), its event needs a **reader**, and that read model needs a **consumer** of its
|
|
69
|
+
own. A command nothing points at is a write nobody can start; an event nothing projects is a
|
|
70
|
+
write nobody can see; a read model nothing displays is information dropped on the floor. Write
|
|
71
|
+
the whole chain together rather than sweeping up dangling ends later. Reactions don't count as
|
|
72
|
+
readers of *events* — they read views. **Every instance** of a repeated read model needs its own
|
|
73
|
+
consumer: if you repeat a view next to an event just to keep the arrow short, bring its screen
|
|
74
|
+
along, or don't add the instance.
|
|
75
|
+
- **Only six connections are legal:** `ui → command`, `command → event`, `event → read model`,
|
|
76
|
+
`read model → ui`, `read model → reaction`, `reaction → command`. Reaching for anything else —
|
|
77
|
+
above all `command → read model` (the CQRS violation) or `read model → command` — means the model
|
|
78
|
+
is missing an **element**, not an arrow.
|
|
56
79
|
- **Validate continuously.** Run `em validate` and fix errors/warnings as you go (see DSL ref).
|
|
57
|
-
- **Save state at the end of every session** so work resumes cleanly.
|
|
80
|
+
- **Save state at the end of every session** so work resumes cleanly. Also append one line to
|
|
81
|
+
the Usage log: the phase(s) touched and the `em validate` diagnostic *categories* that fired
|
|
82
|
+
(rule name only, e.g. "read model nothing consumes" — never the full message or domain
|
|
83
|
+
content). This is the team's only usage signal today (`docs/usage-data.md`) — keep it cheap
|
|
84
|
+
and habitual, not a task to skip.
|
|
58
85
|
|
|
59
86
|
## Preconditions (run first)
|
|
60
87
|
|
|
@@ -113,16 +140,24 @@ phase applies to field tables, below.
|
|
|
113
140
|
they aren't re-proposed later. Capture the survivors as a flat list.
|
|
114
141
|
2. **Plot / storyboard (step 2).** Order the events into the narrative. Identify **personas**
|
|
115
142
|
(actors) and the **UI** screens at each step. Add `persona` declarations and `ui` elements.
|
|
116
|
-
In a **headless/API** model there are no personas or screens — identify the external
|
|
117
|
-
**callers** at each step instead;
|
|
118
|
-
|
|
143
|
+
In a **headless/API** model there are no human personas or screens — identify the external
|
|
144
|
+
**callers** at each step instead; declare one persona per caller/role, and its `ui` boxes are
|
|
145
|
+
the API calls (see `reference/methodology.md`).
|
|
119
146
|
3. **Inputs (step 3).** For each event, find the **command** that causes it (imperative name).
|
|
120
|
-
Form `command → event` slices (State Change pattern).
|
|
147
|
+
Form `command → event` slices (State Change pattern). Every command needs a **trigger** in the
|
|
148
|
+
same breath: the screen the user issues it from (a `ui` in the slice), or — for the Automation
|
|
149
|
+
and Translation patterns — the reaction in the slice before it. A command nothing points at is
|
|
150
|
+
a write nobody can start. *"Who does this, and where are they when they do it?"*
|
|
121
151
|
4. **Outputs (step 4).** Identify the **read models / views** consumers need and wire them with
|
|
122
|
-
`from "Event"` (State View pattern)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`
|
|
152
|
+
`from "Event"` (State View pattern), each with the screen (or reaction) that consumes it — a
|
|
153
|
+
read model nothing displays is information dropped on the floor. In a **headless/API** model
|
|
154
|
+
the consumer is the caller persona's `ui` (the API query) — same as any screen. Read models are
|
|
155
|
+
**repeated** in each slice where they're read (use `view X again` after the first, each instance
|
|
156
|
+
with its own consumer) so the timeline flows (see `reference/methodology.md`).
|
|
157
|
+
**Close the loop before leaving this step:** every event from step 3 must be read by at least one
|
|
158
|
+
read model. Walk the event list and ask, for each one, *"who looks at this, and what do they do
|
|
159
|
+
with it?"* An event with no answer is either missing its read model or shouldn't be recorded —
|
|
160
|
+
both are worth raising with the user rather than leaving dangling.
|
|
126
161
|
|
|
127
162
|
End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (steps done,
|
|
128
163
|
decisions, open questions) and `README.md`'s slice index. Tell the user they can stop here and
|
|
@@ -164,12 +199,17 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
|
|
|
164
199
|
event).
|
|
165
200
|
2. **Elaborate scenarios — first pass (step 6).** For each slice, capture the happy-path
|
|
166
201
|
Given/When/Then and the obvious invariants as short notes. (The full spec is the `slice` phase.)
|
|
167
|
-
3. **Evaluate completeness (step 7).** Walk the model: every
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
202
|
+
3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
|
|
203
|
+
half-slice — **every command has something that triggers it**, every command emits an event,
|
|
204
|
+
**every event is read by a read model**, **every read model has a consumer**, every view has a
|
|
205
|
+
source, every UI is reachable, every connection is
|
|
206
|
+
one of the six legal pairs, and automations **and translations** split correctly (each reaction
|
|
207
|
+
triggers a command — none wired straight to an event). Run `em validate` and resolve all errors
|
|
208
|
+
and warnings — but check the reaction→command→event split by hand, since `em validate` does
|
|
209
|
+
**not** flag a translation/automation that emits an event directly (see `reference/em-dsl.md`).
|
|
210
|
+
For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
|
|
211
|
+
this fact and what they do with it. The honest answers are "here's the read model we missed" or
|
|
212
|
+
"nobody — so why are we recording it", and both improve the model.
|
|
173
213
|
|
|
174
214
|
End of phase: render, update state, suggest `/event-modeling slice` to write implementation specs.
|
|
175
215
|
|
|
@@ -181,7 +221,9 @@ timeline). Check `README.md`'s slice index for what's already done.
|
|
|
181
221
|
This is also where **branch / unhappy-path events** are discovered and added to the model — as a
|
|
182
222
|
slice's alternate/error flows surface (a rejection, removal, cancellation, decline, expiry), add
|
|
183
223
|
the corresponding event/slice to the `.em` and re-render. The happy-path spine from earlier
|
|
184
|
-
phases is the starting point, not the finished event set.
|
|
224
|
+
phases is the starting point, not the finished event set. **Every new event needs its reader too**
|
|
225
|
+
— a rejection or cancellation that nothing projects will warn, and usually the missing piece is
|
|
226
|
+
real (someone has to see that the request was declined).
|
|
185
227
|
|
|
186
228
|
For each slice:
|
|
187
229
|
1. Hold a Socratic deep-dive to fill every section of `templates/slice.md`: intent, trigger/actor,
|
|
@@ -195,12 +237,16 @@ For each slice:
|
|
|
195
237
|
already defined elsewhere.
|
|
196
238
|
2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name). Record the originating
|
|
197
239
|
need (ticket/conversation link) in the Intent section when one exists.
|
|
198
|
-
3.
|
|
240
|
+
3. Render the slice's own diagram: `em render <model>.em --slice "<slice name>" -o
|
|
241
|
+
slices/<slice-name>.svg` (kebab-case, matching the doc's filename) — redraws just this slice
|
|
242
|
+
in its own canonical pattern shape, and add `` near the top of
|
|
243
|
+
the doc, right after the metadata bullet list.
|
|
244
|
+
4. Wire it into the `.em`: add `note "slices/<slice-name>.md"` to the slice's primary element
|
|
199
245
|
(the command for State Change, the view for State View, the processor for Automation, the
|
|
200
246
|
translation for Translation).
|
|
201
|
-
|
|
247
|
+
5. Update `README.md`'s slice index — the one canonical slice table (`draft` →
|
|
202
248
|
`ready-to-implement`, later `implemented` once shipped, with `Implemented in:` filled in).
|
|
203
|
-
|
|
249
|
+
6. Re-render and `em validate`.
|
|
204
250
|
|
|
205
251
|
## Phase: `conform` — drift check against the codebase
|
|
206
252
|
|
|
@@ -238,11 +284,53 @@ appear the moment you save, with no idle churn between edits.
|
|
|
238
284
|
`live.html?svg=<model-name>.svg` in a browser. It polls every ~2s and reloads without flicker. No
|
|
239
285
|
editing of `live.html` is needed — the `?svg=` query picks the model.
|
|
240
286
|
|
|
287
|
+
## Phase: `review` — stakeholder walkthrough
|
|
288
|
+
|
|
289
|
+
Goal: step a real stakeholder review session through the model slice by slice, one slice
|
|
290
|
+
spotlighted at a time, with any open questions the room raises captured live as `issue "..."`
|
|
291
|
+
red notes.
|
|
292
|
+
|
|
293
|
+
**Trigger: a real stakeholder review session is on the calendar — a scheduled use, not
|
|
294
|
+
speculation.** Don't propose this phase proactively; it exists for facilitated reviews with
|
|
295
|
+
non-engineer stakeholders in the room, not routine model editing.
|
|
296
|
+
|
|
297
|
+
Start the same live server as `watch`: `em watch <model-name>.em -o <model-name>.svg --serve`
|
|
298
|
+
(run_in_background). Open the printed URL, click **Review mode** in the header, and share the
|
|
299
|
+
screen. Use Prev/Next (or the left/right arrow keys) to step through slices in declaration
|
|
300
|
+
order — each one pans/zooms into view with everything else dimmed, so the room's attention
|
|
301
|
+
tracks one slice at a time.
|
|
302
|
+
|
|
303
|
+
**Live capture:** when a stakeholder raises something unresolved, add `issue "..."` to the
|
|
304
|
+
relevant element in the `.em` file and save — exactly the same mechanism as any other issue
|
|
305
|
+
(see `validate` below). The browser updates over the existing SSE push within moments, without
|
|
306
|
+
losing the current slice or resetting review mode.
|
|
307
|
+
|
|
308
|
+
Wrap-up: run `em validate --list-issues` to sweep everything captured during the session and
|
|
309
|
+
walk each one with the user, same as any open issue. Update the state file's Participants
|
|
310
|
+
section with who attended, and set a `Last stakeholder review:` marker (mirrors `Last
|
|
311
|
+
conformance:`).
|
|
312
|
+
|
|
313
|
+
End of phase: state file's `Last stakeholder review:` marker updated, every issue captured
|
|
314
|
+
live triaged (resolved on the spot, moved to Open questions / parking lot, or left open on
|
|
315
|
+
purpose). Review doesn't chain to another phase — it's a recurring, scheduled activity like
|
|
316
|
+
`conform`, not a step in the discover → model → slice sequence.
|
|
317
|
+
|
|
241
318
|
## Phase: `validate`
|
|
242
319
|
|
|
243
320
|
Run `em validate <model-name>.em` and walk through each diagnostic with the user, explaining the
|
|
244
|
-
rule (see `reference/em-dsl.md`) and proposing the fix
|
|
245
|
-
|
|
321
|
+
rule (see `reference/em-dsl.md`) and proposing the fix. Apply fixes on agreement. Common ones:
|
|
322
|
+
|
|
323
|
+
| Diagnostic | Fix |
|
|
324
|
+
|---|---|
|
|
325
|
+
| automation/translation shares a slice with a command | Split the command into the next slice |
|
|
326
|
+
| **command has nothing that triggers it** | Add the `ui` it's issued from — or, if the system issues it, the reaction in the slice before it. Ask *"who does this, and where are they when they do it?"* |
|
|
327
|
+
| **read model has no consumer** | Add the screen that displays it, or the reaction that watches it. If it's a repeat added only to shorten an arrow and nothing looks at it there, drop the instance |
|
|
328
|
+
| read model has no source | Add the missing `from "Event"` |
|
|
329
|
+
| command produces no event | Give the command its event, or drop the command |
|
|
330
|
+
| **event is not read by any read model** | Add the read slice that projects it — but ask *who looks at this and what do they do with it* first. "Nobody" is a real answer, and then the question is why it's recorded at all |
|
|
331
|
+
| **illegal `arrow` kind pair** | Don't reroute it — the model is missing an element. `command → view` needs the event between them; `view → command` needs a reaction. Between two instances of one read model, just delete the arrow: the repeat needs no connection |
|
|
332
|
+
| `view X again` with no earlier declaration | Declare the view plainly the first time it appears |
|
|
333
|
+
| event feeding an earlier view instance | Add a `view X again` where the event lands and move the source there |
|
|
246
334
|
|
|
247
335
|
For anything genuinely unresolved rather than a rule violation, prefer `issue "text"` on the
|
|
248
336
|
relevant element over a `# TBD` comment — it shows up as a red marker on the rendered diagram and
|
|
@@ -264,9 +352,12 @@ em init <name>.em # optional starter scaffold
|
|
|
264
352
|
em validate <name>.em # check rules; exit 0 if clean/warnings only
|
|
265
353
|
em render <name>.em -o <name>.svg # render (svg/png/pdf by extension)
|
|
266
354
|
em render <name>.em --emit-dot # inspect generated Graphviz DOT
|
|
355
|
+
em render <name>.em --slice "<slice-name>" -o slices/<slug>.svg # this slice's own diagram
|
|
267
356
|
em watch <name>.em -o <name>.svg # re-render on save (run in background)
|
|
268
357
|
em watch <name>.em -o <name>.svg --serve # + live viewer with instant push-reload (--port N)
|
|
358
|
+
# click "Review mode" for a slice-by-slice storyboard walkthrough
|
|
269
359
|
```
|
|
270
360
|
|
|
271
361
|
Always finish a working session by: re-rendering, running `em validate`, and updating
|
|
272
|
-
`.event-modeling.md` with the current phase/step, decisions, and
|
|
362
|
+
`.event-modeling.md` with the current phase/step, decisions, open questions, and a Usage log
|
|
363
|
+
entry (phases touched + validate diagnostic categories hit).
|
|
@@ -102,7 +102,16 @@ For each slice in scope:
|
|
|
102
102
|
materialized projection in code — a direct event handler with criteria-sourced state is
|
|
103
103
|
the same pattern expressed in the code idiom (extract.md's R5 reaction rules). That is
|
|
104
104
|
NOT structural drift; note the idiom in the evidence log instead of reporting the view
|
|
105
|
-
as missing.
|
|
105
|
+
as missing. This is the *general*, idiom-level version of the next bullet — prefer citing
|
|
106
|
+
an actual `divergence` annotation when the element carries one; fall back to this idiom
|
|
107
|
+
note only when it doesn't.
|
|
108
|
+
- **Accepted divergences:** before treating any finding as drift, check whether the
|
|
109
|
+
relevant canonical element already carries a `divergence "..."` annotation (visible in
|
|
110
|
+
the `.em`, in `em export --json`'s `divergence` field, or — for a structural finding — as
|
|
111
|
+
the `acceptedDivergence` field already attached to that `em diff --json` entry, see step
|
|
112
|
+
3). If it does, this is not a fresh finding to litigate: classify it as **Accepted
|
|
113
|
+
divergence** (step 4) and cite the annotation text as the evidence. This is the specific,
|
|
114
|
+
per-element, ratified counterpart to the idiom-level bullet above.
|
|
106
115
|
3. Write what you found into the scratch model (`<model-name>-asis.em`, see below). **Seed the
|
|
107
116
|
scratch model as a copy of the canonical `.em` and replace only the in-scope slices** with
|
|
108
117
|
the as-is picture, leaving out-of-scope slices byte-identical. This is not an optimization:
|
|
@@ -112,9 +121,13 @@ For each slice in scope:
|
|
|
112
121
|
this phase exists to avoid. It also keeps the scratch model compilable (see step 3). Within
|
|
113
122
|
the slices you do rewrite, **reuse the canonical model's slice and element names wherever
|
|
114
123
|
the code genuinely matches them** — the canonical model is the vocabulary anchor. Give a
|
|
115
|
-
new name only to code behavior the model doesn't cover at all.
|
|
116
|
-
|
|
117
|
-
|
|
124
|
+
new name only to code behavior the model doesn't cover at all. When you do need a new name,
|
|
125
|
+
check `em glossary` across sibling `.em` models first (same aid the `extract` phase uses) —
|
|
126
|
+
if the term already exists elsewhere under a different kind or spelling, that's a signal to
|
|
127
|
+
reuse the established name rather than mint another synonym for the same concept. Record the
|
|
128
|
+
mapping decision for every element you name this way (e.g. `CartItemAdded` code class ↔
|
|
129
|
+
`"Cart Item Added"` model event) so it's auditable, not a silent judgment call. **Match the
|
|
130
|
+
canonical model's field granularity:** declare `{ fields }`
|
|
118
131
|
in the scratch model only where the canonical model declares them (if the canonical model
|
|
119
132
|
puts fields on commands but not events, so does the scratch model — writing code-derived
|
|
120
133
|
event fields there would flood the diff with `field-added` noise). Schema claims at finer
|
|
@@ -135,7 +148,9 @@ what every finding in the report will cite — file paths, not vibes.
|
|
|
135
148
|
First `em validate <model-name>-asis.em`. `em diff` refuses when either side has validation
|
|
136
149
|
*errors* (warnings are fine), so a scratch model that doesn't compile stops the run cold. The
|
|
137
150
|
usual cause is a slice that depends on one you rewrote incompletely — a `view X again` whose
|
|
138
|
-
first declaration you dropped,
|
|
151
|
+
first declaration you dropped, an automation `from` a read model that no longer exists, or an
|
|
152
|
+
`arrow` you wrote joining kinds the patterns don't connect. (An unread event is only a *warning*,
|
|
153
|
+
so it never blocks the diff — and in an as-is scratch model it's expected.)
|
|
139
154
|
Seeding from the canonical model (step 2) prevents this; if it still fails, **fix the scratch
|
|
140
155
|
model and report the failure as a tooling problem in the run metadata — never as drift.** A
|
|
141
156
|
model you couldn't compile is an uncertainty about your own scratch file, not a finding
|
|
@@ -145,7 +160,10 @@ Then run `em diff <model-name>.em <model-name>-asis.em --json`. `em`, not you, d
|
|
|
145
160
|
structurally differs — don't re-derive by eye what the tool already computed. Parse the JSON
|
|
146
161
|
(`docs/cli.md`'s diff section documents the envelope: `diffSchemaVersion`, `counts`, `changes`,
|
|
147
162
|
`removals`, every optional `ChangeEntry` field explicit-`null` when unused) and use it as the
|
|
148
|
-
structural-surface findings directly.
|
|
163
|
+
structural-surface findings directly. Check each entry's `acceptedDivergence` field first: a
|
|
164
|
+
non-null value means `em` itself already found a `divergence` annotation on the canonical
|
|
165
|
+
element behind this entry — that finding is Accepted divergence (step 4), not something to
|
|
166
|
+
re-litigate.
|
|
149
167
|
|
|
150
168
|
### 4. Classify
|
|
151
169
|
|
|
@@ -155,6 +173,11 @@ step 2 — sorts into exactly one class:
|
|
|
155
173
|
- **Real drift** — code evidence contradicts the model or doc.
|
|
156
174
|
- **Model gap** — code evidence shows behavior neither the model nor the doc covers.
|
|
157
175
|
- **Internal inconsistency** — the doc and the `.em` disagree with each other (surface 3 only).
|
|
176
|
+
- **Accepted divergence** — the relevant canonical element carries a `divergence "..."`
|
|
177
|
+
annotation (cited directly from a structural entry's `acceptedDivergence` field, or from the
|
|
178
|
+
`.em`/`em export` for a spec/internal finding). This is what the annotation is *for*: a
|
|
179
|
+
reasoned, ratified deviation stops being re-reported as drift on every run. Cite the
|
|
180
|
+
annotation text, not fresh judgment — that judgment already happened when it was written.
|
|
158
181
|
- **Extraction uncertainty** — no evidence either way. Never reported as drift or a gap.
|
|
159
182
|
|
|
160
183
|
Every classification must cite the evidence recorded in step 2 (or the `em diff` entry, for
|
|
@@ -167,7 +190,9 @@ Write `conformance/<YYYY-MM-DD>-report.md` in the model directory, from
|
|
|
167
190
|
ready-to-apply red note — `issue "conformance: <text>"` on the right element, written out in
|
|
168
191
|
the report exactly as it should be pasted into the `.em` — when a model-side marker is the
|
|
169
192
|
right fix; when the fix is purely doc wording (or the finding is an internal inconsistency
|
|
170
|
-
with no clear side to flag), omit the red note and say why instead. **
|
|
193
|
+
with no clear side to flag), omit the red note and say why instead. **Never propose a red note
|
|
194
|
+
for a finding already classified Accepted divergence** — report it (so the annotation itself
|
|
195
|
+
stays auditable) but don't ask the user to ratify what's already ratified. **You never edit the
|
|
171
196
|
canonical model or a slice doc unprompted** — walk the report with the user, apply only the
|
|
172
197
|
proposals they approve, then re-render and `em validate`.
|
|
173
198
|
|
|
@@ -226,3 +251,7 @@ whatever cadence the user wants (see `docs/ci.md` for a scheduled-run recipe).
|
|
|
226
251
|
- **Re-deriving what `em diff` already decided** — once you have the JSON, use it; don't
|
|
227
252
|
eyeball the two `.em` files yourself looking for structural differences the tool already
|
|
228
253
|
computed for you.
|
|
254
|
+
- **Re-litigating an accepted divergence** — a non-null `acceptedDivergence` on a diff entry
|
|
255
|
+
(or a `divergence` annotation on the element behind a spec/internal finding) means this exact
|
|
256
|
+
question was already reasoned through and ratified. Classify it as Accepted divergence and
|
|
257
|
+
cite the annotation; don't propose a new red note or re-open the judgment call from scratch.
|