@milehimikey/em 1.1.1 → 1.3.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.
Files changed (37) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +74 -25
  2. package/.claude/skills/event-modeling/reference/em-dsl.md +45 -8
  3. package/.claude/skills/event-modeling/reference/extract.md +124 -0
  4. package/.claude/skills/event-modeling/reference/methodology.md +31 -0
  5. package/.claude/skills/event-modeling/templates/live.html +65 -13
  6. package/.claude/skills/event-modeling/templates/model-readme.md +11 -6
  7. package/.claude/skills/event-modeling/templates/slice.md +11 -2
  8. package/.claude/skills/event-modeling/templates/state.md +29 -9
  9. package/README.md +45 -172
  10. package/dist/cli/diff-inputs.d.ts +44 -0
  11. package/dist/cli/diff-inputs.js +62 -0
  12. package/dist/cli/diff-inputs.js.map +1 -0
  13. package/dist/cli.js +150 -7
  14. package/dist/cli.js.map +1 -1
  15. package/dist/emit/json.d.ts +28 -0
  16. package/dist/emit/json.js +121 -0
  17. package/dist/emit/json.js.map +1 -0
  18. package/dist/model/diff.d.ts +52 -0
  19. package/dist/model/diff.js +351 -0
  20. package/dist/model/diff.js.map +1 -0
  21. package/dist/model/model.d.ts +2 -0
  22. package/dist/model/model.js +1 -0
  23. package/dist/model/model.js.map +1 -1
  24. package/dist/model/validate.js +74 -0
  25. package/dist/model/validate.js.map +1 -1
  26. package/dist/parser/ast.d.ts +2 -0
  27. package/dist/parser/parser.js +8 -0
  28. package/dist/parser/parser.js.map +1 -1
  29. package/dist/render/drawNotes.d.ts +8 -2
  30. package/dist/render/drawNotes.js +106 -42
  31. package/dist/render/drawNotes.js.map +1 -1
  32. package/dist/render/render.js +5 -4
  33. package/dist/render/render.js.map +1 -1
  34. package/dist/render/serve.d.ts +20 -0
  35. package/dist/render/serve.js +202 -0
  36. package/dist/render/serve.js.map +1 -0
  37. package/package.json +3 -3
@@ -5,8 +5,10 @@ description: >-
5
5
  the 7 steps of Event Modeling and the 4 patterns (State Change, State View, Automation,
6
6
  Translation), and produce implementation-ready slice design documents. Use when the user wants
7
7
  to event-model a business process or system, do event modeling / event storming, design slices,
8
- build or edit an `.em` model, or run the em tool. Works in resumable phases via an argument:
9
- `discover` (steps 1-4), `model` (steps 5-7), `slice` (deep slice specs), plus `watch` and
8
+ build or edit an `.em` model, run the em tool, or extract / reverse-engineer a current-state
9
+ event model from an existing system or codebase (event-driven or legacy/procedural). Works in
10
+ resumable phases via an argument: `discover` (steps 1-4, greenfield), `extract` (current-state
11
+ model of an existing system), `model` (steps 5-7), `slice` (deep slice specs), plus `watch` and
10
12
  `validate`. With no argument it resumes from the saved state file.
11
13
  ---
12
14
 
@@ -16,8 +18,8 @@ You are facilitating an Event Modeling session. Your job is to **extract an accu
16
18
  the user through Socratic questioning** — never to invent the domain. You drive the `em` CLI to
17
19
  render the model live, and you produce implementation-ready slice design docs.
18
20
 
19
- Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl.md` (DSL syntax
20
- + validation rules) before doing real work — they are the source of truth. Templates live in
21
+ Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl.md` (DSL syntax and
22
+ validation rules) before doing real work — they are the source of truth. Templates live in
21
23
  `templates/`.
22
24
 
23
25
  ## Operating principles (every phase)
@@ -26,7 +28,8 @@ Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl
26
28
  multiple-choice decisions. Never assume a domain fact — extract it. Prefer "who/why/what-if/
27
29
  what-must-always-be-true/how-do-you-know" over yes-no.
28
30
  - **Don't guess — park it.** Unresolved items go into the Open Questions list in
29
- `.event-modeling.md`, not into invented model content.
31
+ `.event-modeling.md`, not into invented model content. Note the source, blocker, and revisit
32
+ date when known — cheap now, useful when you come back to it.
30
33
  - **Happy path first; branches belong to slicing.** Steps 1-7 build the **happy-path spine**.
31
34
  Alternate, unhappy, and exception-path events (rejections, removals, cancellations,
32
35
  expirations, declines) are **not** enumerated as a separate discover/model task — they
@@ -59,7 +62,11 @@ Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl
59
62
  directory (or a `models/` subfolder). If found, read the state file. If not, and the phase
60
63
  needs one, ask the user for the model name and where to create it.
61
64
  3. Parse the argument (`$ARGUMENTS`) to pick the phase below. With **no argument**, read the
62
- state file and resume the recorded phase/step; if no model exists, propose starting `discover`.
65
+ state file and resume the recorded phase/step; if no model exists, propose starting `discover`
66
+ (greenfield) or `extract` (modeling an existing system).
67
+ 4. Populate the state file's Participants section at session start. For a live workshop, ask for
68
+ a single human proxy to relay questions to the room, and attribute every answer/decision in
69
+ the Decisions log to a named participant.
63
70
 
64
71
  ## Project layout this skill creates
65
72
 
@@ -67,22 +74,28 @@ Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl
67
74
  <model-name>/
68
75
  <model-name>.em # the model (slice docs linked via note "...")
69
76
  <model-name>.svg # render target for em watch
70
- live.html # auto-refresh viewer (copy of templates/live.html, SVG_FILE set)
77
+ live.html # file:// auto-refresh viewer (copy of templates/live.html, verbatim)
71
78
  README.md # overview + slice index (from templates/model-readme.md)
72
79
  .event-modeling.md # resumable state (from templates/state.md)
73
80
  slices/<slice-name>.md # one rich slice doc per slice (from templates/slice.md)
74
81
  ```
75
82
 
76
- When creating a new model, scaffold the directory, copy the templates in (filling the
77
- placeholders), and set `SVG_FILE` in the copied `live.html` to `<model-name>.svg`. You may use
78
- `em init` for a starter `.em`, but usually you'll build it up from the discovery conversation
79
- instead. Fill template placeholders — never leave `{{...}}` in delivered files.
83
+ When creating a new model, scaffold the directory and copy the templates in (filling the
84
+ placeholders). Copy `live.html` **verbatim** — it takes the SVG name from its URL query
85
+ (`live.html?svg=<model-name>.svg`), so there's nothing to edit. You may use `em init` for a
86
+ starter `.em`, but usually you'll build it up from the discovery conversation instead. Fill
87
+ template placeholders — never leave `{{...}}` in delivered files.
80
88
 
81
89
  ---
82
90
 
83
91
  ## Phase: `discover` — steps 1-4
84
92
 
85
93
  Goal: a draft model of events, storyboard, commands, and views. Loose is OK; structure comes next.
94
+ **Existing system?** Use `extract` instead (next section) — discover is for greenfield modeling.
95
+ **New feature inside an existing codebase?** Stay in `discover`, but before locking in
96
+ command/event names, Grep/Read adjacent real sources (OpenAPI specs, DB migrations, existing
97
+ DTOs/event classes in sibling contexts) rather than guessing conventions — same check `slice`
98
+ phase applies to field tables, below.
86
99
 
87
100
  1. **Brainstorm events (step 1).** Ask the user to name everything that happens, as past-tense
88
101
  facts. Probe for missed state changes. For each candidate, apply the **"is it an event?"
@@ -104,8 +117,29 @@ Goal: a draft model of events, storyboard, commands, and views. Loose is OK; str
104
117
  `reference/methodology.md`).
105
118
 
106
119
  End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (steps done,
107
- decisions, open questions, slice inventory). Tell the user they can stop here and resume with
108
- `/event-modeling model`.
120
+ decisions, open questions) and `README.md`'s slice index. Tell the user they can stop here and
121
+ resume with `/event-modeling model`.
122
+
123
+ ## Phase: `extract` — current-state model of an existing system
124
+
125
+ Goal: a faithful **current-state** model extracted from an existing system — the as-is sibling
126
+ of `discover` (it replaces discover; the model then proceeds to `model` as usual).
127
+
128
+ **Read `reference/extract.md` before doing any extract work** — it carries a stance override
129
+ that inverts methodology step 1 (capture how the system behaves *today*; never model desired
130
+ state).
131
+
132
+ - Two source modes, detected from the system itself and confirmed with the user:
133
+ **event-driven** (an event vocabulary already exists — schemas, topics, handlers) and
134
+ **procedural/monolith** (no event vocabulary — synthesize candidate events from behavior
135
+ and docs).
136
+ - Converge via a ~7-round confirm-and-clarify loop (one concern per round; render from round 2,
137
+ validate from round 4 — see the playbook).
138
+ - **Current-state-only:** park unknowns as `# TBD` comments in the `.em`, mirrored in the state
139
+ file's Open Questions — never guess intended design.
140
+
141
+ End of phase: validated as-is model; state file updated (source mode, rounds, decisions) and
142
+ `README.md`'s slice index seeded; then chain to `/event-modeling model` (steps 5-7).
109
143
 
110
144
  ## Phase: `model` — steps 5-7
111
145
 
@@ -134,7 +168,7 @@ End of phase: render, update state, suggest `/event-modeling slice` to write imp
134
168
  ## Phase: `slice` — deep slice documents
135
169
 
136
170
  Goal: implementation-ready specs. Go slice by slice (let the user pick order, or follow the
137
- timeline). Check the state file's slice inventory for what's already done.
171
+ timeline). Check `README.md`'s slice index for what's already done.
138
172
 
139
173
  This is also where **branch / unhappy-path events** are discovered and added to the model — as a
140
174
  slice's alternate/error flows surface (a rejection, removal, cancellation, decline, expiry), add
@@ -145,24 +179,33 @@ For each slice:
145
179
  1. Hold a Socratic deep-dive to fill every section of `templates/slice.md`: intent, trigger/actor,
146
180
  command + field table (types & rules), event(s) + payload (mark immutable facts), invariants
147
181
  (give each a stable ID), Given/When/Then scenarios (happy path + rule boundaries + edge cases),
148
- alternate/error flows (retries, idempotency, compensations), read models affected, open
149
- questions. Park anything unresolved rather than guessing.
150
- 2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name).
182
+ alternate/error flows (retries, idempotency, compensations), non-functional requirements
183
+ (security/authz, PII/compliance, performance/SLA), read models affected, open questions. Park
184
+ anything unresolved rather than guessing. If the slice lives in an existing codebase, Grep/Read
185
+ adjacent real sources (OpenAPI specs, DB migrations, existing DTOs/event classes in sibling
186
+ contexts) before finalizing field names/types or invariants — don't guess a shape that's
187
+ already defined elsewhere.
188
+ 2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name). Record the originating
189
+ need (ticket/conversation link) in the Intent section when one exists.
151
190
  3. Wire it into the `.em`: add `note "slices/<slice-name>.md"` to the slice's primary element
152
191
  (the command for State Change, the view for State View, the processor for Automation, the
153
192
  translation for Translation).
154
- 4. Update `README.md`'s slice index and the state file's slice inventory (`draft` → `ready`).
193
+ 4. Update `README.md`'s slice index — the one canonical slice table (`draft` →
194
+ `ready-to-implement`, later `implemented` once shipped, with `Implemented in:` filled in).
155
195
  5. Re-render and `em validate`.
156
196
 
157
197
  ## Phase: `watch` — live team view
158
198
 
159
- 1. Ensure `live.html` exists in the model dir (copy from `templates/live.html`) with `SVG_FILE`
160
- set to `<model-name>.svg`.
161
- 2. Start the watcher in the background: `em watch <model-name>.em -o <model-name>.svg`
162
- (run_in_background). It re-renders the SVG on every save.
163
- 3. Tell the user to open `live.html` in a browser and share their screen — it auto-refreshes ~1s,
164
- so the team sees the model evolve as you edit during the session. (`em` has no built-in server;
165
- the HTML viewer provides the auto-reload.)
199
+ **Recommended (instant push, no polling):** start the watcher with `--serve` in the background:
200
+ `em watch <model-name>.em -o <model-name>.svg --serve` (run_in_background). It re-renders on every
201
+ save and pushes an instant reload to the browser over Server-Sent Events. Tell the user to open the
202
+ URL it prints (e.g. `http://localhost:5173/?svg=<model-name>.svg`) and share their screen — updates
203
+ appear the moment you save, with no idle churn between edits.
204
+
205
+ **No-server fallback (`file://`):** if the user prefers not to run a server, start the plain watcher
206
+ `em watch <model-name>.em -o <model-name>.svg` (run_in_background) and have them open the copied
207
+ `live.html?svg=<model-name>.svg` in a browser. It polls every ~2s and reloads without flicker. No
208
+ editing of `live.html` is needed — the `?svg=` query picks the model.
166
209
 
167
210
  ## Phase: `validate`
168
211
 
@@ -170,6 +213,11 @@ Run `em validate <model-name>.em` and walk through each diagnostic with the user
170
213
  rule (see `reference/em-dsl.md`) and proposing the fix (e.g. split an automation's command into
171
214
  the next slice, add a missing `from`, give a command its event). Apply fixes on agreement.
172
215
 
216
+ For anything genuinely unresolved rather than a rule violation, prefer `issue "text"` on the
217
+ relevant element over a `# TBD` comment — it shows up as a red marker on the rendered diagram and
218
+ `em validate` tracks it as an open-issue warning, so it isn't lost once rendered. `em validate
219
+ --list-issues` gives a quick sweep of everything still open.
220
+
173
221
  Then do one check `em validate` can't: scan every `translation`/`processor`/`automation` slice and
174
222
  confirm none contains an `event` — each reaction must trigger a `command` in the next slice
175
223
  (`reaction → command → event`). If you find a reaction wired straight to an event, split it into
@@ -186,6 +234,7 @@ em validate <name>.em # check rules; exit 0 if clean/warnin
186
234
  em render <name>.em -o <name>.svg # render (svg/png/pdf by extension)
187
235
  em render <name>.em --emit-dot # inspect generated Graphviz DOT
188
236
  em watch <name>.em -o <name>.svg # re-render on save (run in background)
237
+ em watch <name>.em -o <name>.svg --serve # + live viewer with instant push-reload (--port N)
189
238
  ```
190
239
 
191
240
  Always finish a working session by: re-rendering, running `em validate`, and updating
@@ -1,3 +1,5 @@
1
+ <!-- DSL behavior change? Update BOTH docs/dsl.md and .claude/skills/event-modeling/reference/em-dsl.md -->
2
+
1
3
  # `em` DSL Reference & Cheatsheet
2
4
 
3
5
  The `em` tool (`@milehimikey/em`) is a slice-first text DSL rendered to a strict Graphviz
@@ -13,8 +15,11 @@ em init [file] # scaffold a starter model.em (default: model.em)
13
15
  em render <file> -o out.svg # render (extension picks format: svg, png, pdf)
14
16
  em render <file> --emit-dot # print the Graphviz DOT instead of rendering
15
17
  em render <file> --keep-empty-lanes # keep the API lane even when empty
16
- em watch <file> -o out.svg # re-render on every save (file-based; no HTTP server)
18
+ em watch <file> -o out.svg # re-render on every save (file-based)
19
+ em watch <file> -o out.svg --serve # + localhost live viewer, instant SSE push-reload (--port N)
17
20
  em validate <file> # check event-modeling rules; exit 0 if only warnings/clean
21
+ em validate <file> --list-issues # print only open `issue "..."` clauses (slice, element, line, text)
22
+ em validate <file> --fail-on-issues # opt-in CI gate: exit non-zero while any issue remains open
18
23
  ```
19
24
 
20
25
  Install if missing: `npm i -g @milehimikey/em`. PNG works with no system deps; PDF needs
@@ -34,6 +39,7 @@ slice "Name" { # one vertical time step (a column)
34
39
  ui Free Text @Persona # screen; @Persona picks its row (defaults to first/"User")
35
40
  command Free Text # state-changing request (API band)
36
41
  view Free Text from "Event A", "Event B" # read model fed by event(s)
42
+ view Free Text again from "Event C" # later instance of an evolving read model (see Clauses)
37
43
  event Free Text @Context # recorded fact; @Context picks its row (defaults to "Domain")
38
44
  processor Free Text from "View" # automation; aliases: automation | saga | translation
39
45
  }
@@ -44,18 +50,32 @@ arrow From Element -> To Element # explicit cross-slice edge (overrides infer
44
50
  ### Element kinds (8 keywords, nothing else)
45
51
  | Keyword | Band | Meaning | Tag | Extra clauses |
46
52
  |---|---|---|---|---|
47
- | `ui` | persona | screen / interface | `@Persona` | `note`, `{ fields }` |
48
- | `command` | API | state-changing request | — | `note`, `{ fields }` |
49
- | `view` | API | read model / projection | — | `from "Event"…`, `note`, `{ fields }` |
50
- | `event` | context | recorded fact (past tense) | `@Context` | `note`, `{ fields }` |
51
- | `processor` / `automation` / `saga` / `translation` | automation | system reaction / adapter | — | `from "…"`, `note`, `{ fields }` |
53
+ | `ui` | persona | screen / interface | `@Persona` | `note`, `issue`, `{ fields }` |
54
+ | `command` | API | state-changing request | — | `note`, `issue`, `{ fields }` |
55
+ | `view` | API | read model / projection | — | `from "Event"…`, `note`, `issue`, `{ fields }` |
56
+ | `event` | context | recorded fact (past tense) | `@Context` | `note`, `issue`, `{ fields }` |
57
+ | `processor` / `automation` / `saga` / `translation` | automation | system reaction / adapter | — | `from "…"`, `note`, `issue`, `{ fields }` |
52
58
 
53
59
  ### Clauses
54
60
  - **Tags:** `@Persona` only on `ui`; `@Context` only on `event`. Undeclared tags auto-create a row.
55
61
  - **`from "X"`** on views/automations declares the source(s); names are quoted, comma-separated.
56
- Matching is case-insensitive and whitespace-normalized.
62
+ Matching is case-insensitive and whitespace-normalized. A `from` may never point at an event or
63
+ view that first appears in a LATER slice (forward-only timeline — validation error).
64
+ - **`again`** (views only): `view <Name> again [from "Event", …]` declares a later instance of an
65
+ already-declared read model — the forward-only device for a view that evolves as later events
66
+ land. Instances are ONE logical view: the first declaration owns the `note` binding; each
67
+ instance's `from` lists only the NEW events landing at that point (not cumulative); instances are
68
+ linked left-to-right with a continuity arrow; a reaction (`from "View"`) reads the nearest
69
+ instance at-or-before its own slice. `again` with no earlier declaration is a validation error.
70
+ Use `again` (not a plain repeated `view` name) whenever the view is referenced by a
71
+ `from`/`arrow` — plain repeats are only warning-free while unreferenced.
57
72
  - **`note "path.md"`** on ANY element links a markdown doc. Relative to the `.em` file. Renders as
58
73
  a clickable marker in SVG and a legend entry in PNG/PDF. **This is how slice docs attach.**
74
+ - **`issue "text"`** on ANY element flags an open question inline — the diagram-visible red
75
+ sticky note. Renders as a red corner marker (opposite corner from `note`, so both can coexist
76
+ on one element) plus a legend entry; `em validate` warns on every open issue. **Prefer this
77
+ over a `# TBD` comment for anything that should show up on the rendered diagram** — `# TBD` is
78
+ invisible once rendered, `issue` isn't.
59
79
  - **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
60
80
  one-per-line. Types are free text (no semantic checking). Keep these light; full field specs
61
81
  with rules live in the slice doc.
@@ -141,7 +161,9 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
141
161
  *referenced* duplicate, resolving to the first). **Wire each event to a read model exactly once:**
142
162
  a repeated instance's `from` lists only the **new** events since the previous instance (not
143
163
  cumulative), or the event draws a duplicate arrow to the same read model at every repeat. (An
144
- event may still feed several *different* read models, once each.)
164
+ event may still feed several *different* read models, once each.) When a repeated view **must be
165
+ referenced** — e.g. a reaction reads it via `from` — declare its later instances with
166
+ `view X again` instead (see Clauses); instances resolve properly and draw continuity arrows.
145
167
  - **Keep arrows span-1: put each repeat right after its feeding event.** Place a read-model instance
146
168
  immediately after the event that updates it, sourcing only that single adjacent event. A read
147
169
  model far from its source events draws long arrows that cross the read-model row and read as
@@ -170,6 +192,11 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
170
192
  2. **Unknown event source** — `view X from "Event"` where the event doesn't exist anywhere.
171
193
  3. **Unknown read-model source** — `processor X from "View"` where the view doesn't exist.
172
194
  4. **Arrow endpoint mismatch** — `arrow A -> B` where A or B matches no element name.
195
+ 5. **Backward timeline** ("time flows left to right") — an event feeding a view instance in an
196
+ EARLIER slice (fix: add a `view X again` instance where the event lands and move the source
197
+ there), a reaction reading a view before any instance of it exists, or an explicit backward
198
+ `arrow`.
199
+ 6. **`again` without an earlier declaration** — declare the view plainly the first time it appears.
173
200
 
174
201
  **Warnings (should fix):**
175
202
  1. **Automation/translation shares slice with its command** — both `automation`/`processor` and
@@ -177,6 +204,16 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
177
204
  2. **Command without event** — every command should record at least one event.
178
205
  3. **Read model without source** — add `from "Event"` or place the view in a slice with an event.
179
206
  4. **Duplicate name** — the same name defined N times; references resolve to the first. Rename.
207
+ 5. **Open issue** — an element carries `issue "text"`; resolve the question, then remove the
208
+ clause. `em validate --list-issues` prints just these; `--fail-on-issues` (opt-in) makes CI
209
+ fail while any remain open.
210
+ 6. **View field with no source** — a `view` field whose name matches no field on any instance
211
+ of its source events. Only checked once BOTH the view and at least one source event declare
212
+ `{ fields }`.
213
+ 7. **Event field not from a command** — an `event` field whose name matches no field on any
214
+ command in the same slice. Only checked once BOTH the event and at least one same-slice
215
+ command declare `{ fields }`. This is the payoff of the fields feature for slicing rigor:
216
+ once fields are written down, `em validate` checks that data flows forward consistently.
180
217
 
181
218
  **Design rules that keep models valid:**
182
219
  - One element per band per slice (multiple personas/contexts are fine — they're different rows).
@@ -0,0 +1,124 @@
1
+ # Extract Phase — Current-State Model of an Existing System
2
+
3
+ Read this **before doing any extract work**. It governs the `extract` phase and **overrides
4
+ `methodology.md` step 1's stance** for the duration of extraction: you are capturing how the
5
+ system behaves **today**, not modeling what the business needs. Everything else in the
6
+ methodology — the 4 patterns, the "is it an event?" test, the Socratic stance, the two-slice
7
+ reaction split — still applies.
8
+
9
+ ## Stance override — current-state-only
10
+
11
+ - Capture the system **as it is**, faithfully, warts included. Never invent future or desired
12
+ state. Improvement ideas that surface go to the state file's Open Questions or Decisions log
13
+ — never into the model.
14
+ - Methodology step 1's caution ("model the process the business **needs**, not how the current
15
+ system happens to work — existing system behavior is a common source of fake events") is
16
+ **deliberately suspended**: here the existing system *is* the subject.
17
+ - The **"is it an event?" filter still applies.** Derived values (totals, calculated fields)
18
+ and telemetry/activity (clicks, page views, logins) are still not events, even when the
19
+ current system stores or emits them as such. Park each rejection in the Decisions log with
20
+ why, so it isn't re-proposed.
21
+ - **Never guess.** Unknown or ambiguous current behavior is parked (see TBD convention below),
22
+ not filled in with what the design "probably" intends.
23
+
24
+ ## Setup
25
+
26
+ 1. Run the SKILL.md preconditions (tool check, model location) and scaffold the standard
27
+ project layout if no model exists yet.
28
+ 2. **Agree the scope line first**: which system, which part, what's out of bounds. Write it to
29
+ the state file's Session inputs.
30
+ 3. Record in the state file: `Source mode` (after detection, below) and
31
+ `Existing system refs` — the repo paths, event-schema/topic locations, and docs you will
32
+ extract from.
33
+
34
+ ## Mode detection
35
+
36
+ Inspect the system with your own tools (Grep/Read over the codebase and docs), then confirm
37
+ the mode with the user:
38
+
39
+ - **Event-driven** — the system already has an event vocabulary. Signals: event
40
+ schemas/classes, topics/streams/queues, message or CDC handlers, an event store, pub/sub
41
+ wiring. Extraction *lifts* the model from that vocabulary.
42
+ - **Procedural / monolith** — no event vocabulary. Signals: transaction scripts, service
43
+ methods mutating rows, CRUD controllers, procedural runbooks/docs. Extraction *synthesizes*
44
+ candidate events from the state changes the behavior implies.
45
+
46
+ Mixed systems happen: pick the dominant mode, note the exception areas in the state file.
47
+
48
+ ## Sourcing by mode
49
+
50
+ - **Event-driven:** inventory the emitted/consumed event types; take past-tense business facts
51
+ directly; then filter — technical/infrastructure messages (retries, heartbeats, envelope
52
+ wrappers) and derived/telemetry types are dropped (log why). Keep the system's own names
53
+ where they are honest past-tense facts; propose a cleaner past-tense name only when the
54
+ original is technical (record the mapping in the Decisions log).
55
+ - **Procedural:** for each significant operation, ask "what business fact has become true when
56
+ this completes?" — those are the candidate events. Screens and tables are *evidence* of
57
+ state changes, not events themselves; a table row's lifecycle (created/updated/closed) often
58
+ implies several events. Apply the event filter hard here — procedural systems are where
59
+ derived values and activity records masquerade as facts.
60
+
61
+ ## The confirm-and-clarify loop (~7 rounds)
62
+
63
+ One concern per round. After every round: mirror what you extracted back to the user, update
64
+ the `.em`, and (from R2) re-render so they see the model grow. Convergence beats coverage —
65
+ it is fine to finish a round with `# TBD`s still parked.
66
+
67
+ - **R1 — Candidate events.** Extract (event-driven) or synthesize (procedural) the past-tense
68
+ facts; apply the event test; user confirms/renames/rejects. Ambiguous candidates → `# TBD`.
69
+ - **R2 — Timeline order.** Order the confirmed events into the as-is narrative. Identify the
70
+ *actual* actors/callers and the *real* UI or API surfaces (headless systems: no
71
+ `ui`/`persona` — use write/read translations per `em-dsl.md`). **First `em render` here.**
72
+ - **R3 — Commands / inputs.** For each event, the request that actually produces it today
73
+ (imperative name). Unclear trigger → `# TBD`, not a plausible guess.
74
+ - **R4 — Read models / outputs.** The projections/queries the system actually serves; wire
75
+ `from "Event"` (repeat read models span-1 per `em-dsl.md`). **Start running `em validate`**
76
+ each round from here.
77
+ - **R5 — Boundaries & reactions.** Current automations and integrations as-is: schedulers,
78
+ listeners, outbound syncs, inbound webhooks. Model each as a reaction with the two-slice
79
+ `reaction → command → event` split and the slice-ordering rules in `em-dsl.md` — even when
80
+ the current code wires it as one procedural step (note the as-built shape in the slice's
81
+ `# TBD`/Decisions if it differs).
82
+ - **R6 — Gap & TBD reconciliation.** Walk every parked `# TBD` with the user. Resolve only
83
+ what they can confirm **as a current-state fact**; everything else stays parked. Explicitly
84
+ refuse to resolve a TBD by inventing desired behavior.
85
+ - **R7 — Convergence.** Final `em render` + `em validate` (fix errors and warnings; remember
86
+ the validator's blind spot — check the reaction split by hand). User confirms the model is a
87
+ faithful picture of today. Mark extraction complete in the state file and hand off.
88
+
89
+ Seven rounds is the default shape, not a quota — collapse rounds for a small system, repeat
90
+ one for a sprawling one. Record any deviation in the Decisions log.
91
+
92
+ ## TBD parking convention
93
+
94
+ - Inline `.em` comment on its own line directly above (or trailing) the element it concerns:
95
+ `# TBD: <specific question about current behavior>`. Comments are native DSL syntax — they
96
+ render and validate cleanly.
97
+ - Mirror every `# TBD` 1:1 as a checkbox in the state file's **Open questions / parking lot** —
98
+ add `source`/`blocked on`/`revisit` metadata when known — and remove both together when
99
+ resolved.
100
+ - A `# TBD` is only ever resolved by a confirmed current-state fact — never replaced by a
101
+ guessed element.
102
+
103
+ ## Completion & handoff
104
+
105
+ Extraction is complete when: the model renders; `em validate` is clean (or remaining warnings
106
+ are consciously accepted in the Decisions log); every unknown is a parked `# TBD`; and the
107
+ user has confirmed the model reflects today's behavior. Then:
108
+
109
+ 1. State file: set phase progress (extraction rounds checked), log the completion decision.
110
+ Seed `README.md`'s **slice index** (every slice, pattern, status `none`).
111
+ 2. The extracted model **replaces `discover`** — suggest `/event-modeling model` next
112
+ (steps 5-7: swimlanes, patterns, completeness). Desired-state changes belong *after* the
113
+ as-is model is agreed — as normal `model`/`slice` evolution, clearly separated from
114
+ extraction.
115
+
116
+ ## Anti-patterns
117
+
118
+ - **Inventing desired state** — the cardinal sin of extraction; park it instead.
119
+ - **Importing screens/tables as events** — they are evidence of state changes, not facts.
120
+ - **Guessing an unclear trigger or reaction** instead of parking a `# TBD`.
121
+ - **Cleaning up the domain while extracting** — renames and restructures beyond honest
122
+ past-tense naming are `model`-phase work, done with the user after the as-is picture exists.
123
+ - **Skipping render/validate until the end** — the loop depends on the user seeing the model
124
+ grow round by round.
@@ -121,6 +121,9 @@ state change anyone can think of. No commands, no UI yet — just facts.
121
121
  User Clicked Button, User Viewed Page, User Logged In.
122
122
  - Model the process the business **needs**, not how the current system happens to work. Existing
123
123
  system behavior is a common source of fake events — screens and tables leaking in as facts.
124
+ (**Extract mode inverts this**: in the skill's `extract` phase the existing system *is* the
125
+ subject and this caution is deliberately suspended — see the "Extract" section below and
126
+ `reference/extract.md`. The derived-value and telemetry filters still apply there.)
124
127
  - Output: a flat list of candidate `event` names.
125
128
 
126
129
  ### Step 2 — The Plot / Storyboard *(discover)*
@@ -160,6 +163,9 @@ dedicated `slice` phase writes the full rich spec (see `templates/slice.md`).
160
163
  - Prompts: *"Given what starting state, when this command/trigger fires, then what event(s)
161
164
  result? What must always be true? What are the failure paths? Which fields are essential and
162
165
  what are their rules?"*
166
+ - **Existing codebase:** before finalizing field names/types, check adjacent real sources
167
+ (OpenAPI specs, DB migrations, existing DTOs/event classes in sibling contexts) rather than
168
+ guessing — mirrors `extract.md`'s whole-system grounding, applied per-slice.
163
169
  - Output: rich slice docs linked into the `.em` via `note "slices/<name>.md"`.
164
170
 
165
171
  ### Step 7 — Evaluate Completeness *(model)*
@@ -178,6 +184,28 @@ Walk the whole model with stakeholders and check for loose ends:
178
184
 
179
185
  ---
180
186
 
187
+ ## Extract: current-state models of existing systems
188
+
189
+ The `extract` phase derives a model from an **existing** system instead of a greenfield
190
+ conversation. It is discover's as-is sibling: its confirm-and-clarify rounds converge the same
191
+ step 1–4 outputs (events, timeline, commands, read models) — *derived from the system*, then
192
+ confirmed with the user — after which the model enters `model` (steps 5–7) as usual.
193
+
194
+ The stance inversion, stated authoritatively: **extraction captures how the system behaves
195
+ today — faithfully, warts included — and never invents future or desired state.** Step 1's
196
+ anti-current-system caution is suspended for the duration; the "is it an event?" filter is
197
+ not — derived values and telemetry are still rejected even when the current system emits them.
198
+ Unknown or ambiguous current behavior is parked, never guessed — mirrored in the state file's
199
+ Open Questions, and on the element itself with `issue "text"` when it should also show up on the
200
+ rendered diagram (preferred over a bare `# TBD` comment, which is invisible once rendered).
201
+ Desired-state improvements belong to the `model` and `slice` phases, after the as-is picture is
202
+ agreed.
203
+
204
+ Procedure, source modes (event-driven vs. procedural synthesis), and the round-by-round loop:
205
+ `reference/extract.md`.
206
+
207
+ ---
208
+
181
209
  ## Socratic stance (applies throughout)
182
210
 
183
211
  - Ask **one focused question at a time**; never assume a domain fact — extract it.
@@ -185,3 +213,6 @@ Walk the whole model with stakeholders and check for loose ends:
185
213
  - Mirror the model back after each increment and **re-render** so the team sees it evolve.
186
214
  - Park unresolved questions in the state file instead of guessing.
187
215
  - Name things crisply: events past-tense, commands imperative, views as the thing-shown.
216
+ - **Live workshop, one chat interface:** route through a single human proxy who relays questions
217
+ to the room and reports answers back; attribute every answer/decision in the Decisions log to
218
+ the named participant who gave it (see the state file's Participants section).
@@ -3,10 +3,22 @@
3
3
  Live viewer for an em event model. Auto-reloads the rendered SVG so a team on a shared
4
4
  screen sees updates from `em watch` without manual refresh.
5
5
 
6
- Usage:
7
- 1) In one terminal: em watch MODEL.em -o MODEL.svg
8
- 2) Set SVG_FILE below to the SVG filename (same folder as this file).
9
- 3) Open this file in a browser (open live.html) and share the screen.
6
+ Two ways to use it:
7
+
8
+ A) Push (recommended, instant, no polling):
9
+ em watch MODEL.em -o MODEL.svg --serve
10
+ then open the URL it prints (http://localhost:5173/?svg=MODEL.svg). You don't need
11
+ this file at all in that mode — the server serves its own push-based viewer.
12
+
13
+ B) This file, over file:// (no server):
14
+ 1) In one terminal: em watch MODEL.em -o MODEL.svg
15
+ 2) Open this file with the SVG name in the query:
16
+ open "live.html?svg=MODEL.svg" (macOS)
17
+ No editing required — the ?svg= param picks the model.
18
+
19
+ Under file:// the browser gives no reliable "did the file change?" signal (fetch/HEAD is
20
+ blocked), so this fallback polls and reloads on a gentle interval. It double-buffers the
21
+ swap so a shared screen never flashes. For zero idle work and instant updates, use --serve.
10
22
 
11
23
  The SVG is embedded via <object> so note "..." links inside it stay clickable.
12
24
  -->
@@ -37,17 +49,57 @@
37
49
  <object id="svg" type="image/svg+xml"></object>
38
50
  </div>
39
51
  <script>
40
- // Change this to your model's SVG filename:
41
- const SVG_FILE = "model.svg";
42
- const obj = document.getElementById("svg");
52
+ // SVG filename from ?svg=<name>.svg (defaults to model.svg) — one file serves any model.
53
+ const SVG_FILE = new URLSearchParams(location.search).get("svg") || "model.svg";
54
+ // Poll cadence for the file:// fallback. Tune freely; --serve makes polling unnecessary.
55
+ const REFRESH_INTERVAL_MS = 2000;
56
+
57
+ const stage = document.getElementById("stage");
43
58
  const stamp = document.getElementById("stamp");
44
- function reload() {
45
- // cache-bust so the freshly written SVG is fetched each tick
46
- obj.setAttribute("data", SVG_FILE + "?t=" + Date.now());
47
- stamp.textContent = new Date().toLocaleTimeString();
59
+ let current = document.getElementById("svg");
60
+ let lastModified = null;
61
+
62
+ // Best-effort change check — only meaningful when served over http(s). Under file://
63
+ // fetch/HEAD is blocked, so this throws and we fall back to reloading every tick.
64
+ async function changed() {
65
+ if (!location.protocol.startsWith("http")) return true;
66
+ try {
67
+ const res = await fetch(SVG_FILE, { method: "HEAD", cache: "no-store" });
68
+ const tag = res.headers.get("last-modified") || res.headers.get("etag");
69
+ if (tag && tag === lastModified) return false;
70
+ lastModified = tag;
71
+ return true;
72
+ } catch {
73
+ return true; // can't tell — reload to be safe
74
+ }
48
75
  }
49
- reload();
50
- setInterval(reload, 1000);
76
+
77
+ // Double-buffer: load into a hidden <object>, swap on load, so the shared screen
78
+ // never flashes white during a reload.
79
+ function swap() {
80
+ const next = document.createElement("object");
81
+ next.type = "image/svg+xml";
82
+ next.style.cssText = current.style.cssText;
83
+ next.addEventListener("load", () => {
84
+ stage.replaceChild(next, current);
85
+ current = next;
86
+ stamp.textContent = new Date().toLocaleTimeString();
87
+ }, { once: true });
88
+ next.setAttribute("data", SVG_FILE + "?t=" + Date.now());
89
+ }
90
+
91
+ // Self-scheduling loop: schedule the next tick only after this one resolves, so a slow
92
+ // reload can never overlap the next tick.
93
+ async function tick() {
94
+ try {
95
+ if (await changed()) swap();
96
+ } finally {
97
+ setTimeout(tick, REFRESH_INTERVAL_MS);
98
+ }
99
+ }
100
+
101
+ swap();
102
+ setTimeout(tick, REFRESH_INTERVAL_MS);
51
103
  </script>
52
104
  </body>
53
105
  </html>
@@ -6,10 +6,13 @@
6
6
  While modeling, run the live view so the team can watch the diagram update:
7
7
 
8
8
  ```bash
9
- em watch {{model-name}}.em -o {{model-name}}.svg # re-renders on every save
10
- # then open live.html in a browser (auto-refreshes ~1s; share the screen)
9
+ em watch {{model-name}}.em -o {{model-name}}.svg --serve # re-render + instant push-reload
10
+ # then open the URL it prints (http://localhost:5173/?svg={{model-name}}.svg) and share the screen
11
11
  ```
12
12
 
13
+ No server? Run `em watch {{model-name}}.em -o {{model-name}}.svg` and open
14
+ `live.html?svg={{model-name}}.svg` in a browser (polls ~2s; share the screen).
15
+
13
16
  Static render: `em render {{model-name}}.em -o {{model-name}}.svg`
14
17
 
15
18
  ## Patterns legend
@@ -19,10 +22,12 @@ Static render: `em render {{model-name}}.em -o {{model-name}}.svg`
19
22
  - **Translation** — External input (or Read Model) → Translation → (next slice) Command → Event
20
23
 
21
24
  ## Slices
22
- <!-- Keep this index in sync as slice docs are written. -->
23
- | # | Slice | Pattern | Design doc |
24
- |---|-------|---------|------------|
25
- | 1 | {{Slice Name}} | {{Pattern}} | [slices/{{slice-name}}.md](slices/{{slice-name}}.md) |
25
+ <!-- The canonical slice index — the ONE place slices are enumerated (the state file
26
+ points here rather than keeping its own copy). Keep it in sync as slice docs are
27
+ written and as statuses move. -->
28
+ | # | Slice | Pattern | Status | Design doc |
29
+ |---|-------|---------|--------|------------|
30
+ | 1 | {{Slice Name}} | {{Pattern}} | {{none / draft / reviewed / ready-to-implement / implemented}} | [slices/{{slice-name}}.md](slices/{{slice-name}}.md) |
26
31
 
27
32
  ## Status
28
33
  See [`.event-modeling.md`](.event-modeling.md) for current phase, decisions, and open questions.
@@ -9,10 +9,12 @@ Replace the bracketed placeholders; delete guidance comments before finishing.
9
9
 
10
10
  - **Pattern:** {{State Change | State View | Automation | Translation}}
11
11
  - **Swimlane:** {{Persona/Actor}} → {{Context/Aggregate}}
12
- - **Status:** {{draft | reviewed | ready-to-implement}}
12
+ - **Status:** {{draft | reviewed | ready-to-implement | implemented}}
13
+ - **Implemented in:** {{PR/commit link — fill in once status is `implemented`}}
13
14
 
14
15
  ## Intent
15
- {{Why this slice exists — the user or business goal it serves, in one or two sentences.}}
16
+ {{Why this slice exists — the user or business goal it serves, in one or two sentences. Note the
17
+ originating ticket/conversation link here if one exists.}}
16
18
 
17
19
  ## Trigger & Actor
18
20
  {{Who or what initiates this slice and under what circumstances. For automations, the watched
@@ -61,6 +63,13 @@ an event directly.}}
61
63
  - {{e.g. external call fails → retry policy / compensating event}}
62
64
  - {{idempotency: what happens if the command/event arrives twice?}}
63
65
 
66
+ ## Non-Functional Requirements
67
+ <!-- Short checklist. Idempotency is covered above under Alternate & Error Flows — not repeated
68
+ here. -->
69
+ - **Security / authz:** {{who may invoke this; role/permission checks — or "none"}}
70
+ - **PII & compliance:** {{personal data touched, retention/consent constraints — or "none"}}
71
+ - **Performance / SLA:** {{latency/throughput expectation — or "none"}}
72
+
64
73
  ## Dependencies & Read Models Affected
65
74
  - **Upstream events this slice relies on:** {{...}}
66
75
  - **Downstream read models / slices affected:** {{...}}