@milehimikey/em 1.13.1 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/em-critic.md +26 -0
- package/.claude/agents/em-implementer.md +34 -0
- package/.claude/agents/em-reviewer.md +31 -0
- package/.claude/agents/em-validator.md +27 -0
- package/.claude/skills/event-modeling/SKILL.md +50 -11
- package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
- package/.claude/skills/event-modeling-design/SKILL.md +127 -18
- package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
- package/.claude/skills/event-modeling-engagement/SKILL.md +192 -0
- package/.claude/skills/event-modeling-engagement/reference/engagement.md +143 -0
- package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
- package/.claude/skills/event-modeling-implement/reference/implement.md +38 -11
- package/.claude/skills/event-modeling-review/SKILL.md +2 -2
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +279 -189
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +61 -0
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +116 -5
- package/.claude/skills/event-modeling-shared/templates/constitution.md +18 -1
- package/.claude/skills/event-modeling-shared/templates/slice.md +64 -39
- package/.claude/skills/event-modeling-shared/templates/state.md +5 -1
- package/.claude-plugin/marketplace.json +14 -0
- package/README.md +25 -11
- package/dist/catalog/apiFirst.d.ts +28 -0
- package/dist/catalog/apiFirst.js +49 -0
- package/dist/catalog/apiFirst.js.map +1 -0
- package/dist/catalog/docJoin.d.ts +13 -0
- package/dist/catalog/docJoin.js +11 -0
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/docModelConsistencyValidate.js +44 -4
- package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
- package/dist/catalog/invariantsValidate.d.ts +4 -0
- package/dist/catalog/invariantsValidate.js +102 -0
- package/dist/catalog/invariantsValidate.js.map +1 -0
- package/dist/catalog/scenarios.d.ts +15 -0
- package/dist/catalog/scenarios.js +97 -0
- package/dist/catalog/scenarios.js.map +1 -0
- package/dist/catalog/sliceDoc.d.ts +8 -0
- package/dist/catalog/sliceDoc.js +9 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/catalog/sliceReadyValidate.d.ts +31 -4
- package/dist/catalog/sliceReadyValidate.js +66 -8
- package/dist/catalog/sliceReadyValidate.js.map +1 -1
- package/dist/catalog/sliceSections.d.ts +55 -0
- package/dist/catalog/sliceSections.js +317 -0
- package/dist/catalog/sliceSections.js.map +1 -0
- package/dist/cli/agentsMd.js +7 -1
- package/dist/cli/agentsMd.js.map +1 -1
- package/dist/cli/api.d.ts +90 -0
- package/dist/cli/api.js +318 -0
- package/dist/cli/api.js.map +1 -0
- package/dist/cli/ciInit.d.ts +82 -21
- package/dist/cli/ciInit.js +468 -64
- package/dist/cli/ciInit.js.map +1 -1
- package/dist/cli/conformScope.d.ts +28 -0
- package/dist/cli/conformScope.js +67 -0
- package/dist/cli/conformScope.js.map +1 -1
- package/dist/cli/coverage.d.ts +9 -0
- package/dist/cli/coverage.js +60 -8
- package/dist/cli/coverage.js.map +1 -1
- package/dist/cli/defer.d.ts +55 -0
- package/dist/cli/defer.js +244 -0
- package/dist/cli/defer.js.map +1 -0
- package/dist/cli/engagement.d.ts +210 -0
- package/dist/cli/engagement.js +444 -0
- package/dist/cli/engagement.js.map +1 -0
- package/dist/cli/engagementFile.d.ts +86 -0
- package/dist/cli/engagementFile.js +255 -0
- package/dist/cli/engagementFile.js.map +1 -0
- package/dist/cli/metrics.d.ts +10 -0
- package/dist/cli/metrics.js +49 -1
- package/dist/cli/metrics.js.map +1 -1
- package/dist/cli/pluginPin.d.ts +55 -0
- package/dist/cli/pluginPin.js +188 -0
- package/dist/cli/pluginPin.js.map +1 -0
- package/dist/cli/ratify.d.ts +27 -2
- package/dist/cli/ratify.js +112 -13
- package/dist/cli/ratify.js.map +1 -1
- package/dist/cli/reratify.d.ts +4 -2
- package/dist/cli/reratify.js +41 -16
- package/dist/cli/reratify.js.map +1 -1
- package/dist/cli/scopeInputs.d.ts +48 -0
- package/dist/cli/scopeInputs.js +176 -0
- package/dist/cli/scopeInputs.js.map +1 -0
- package/dist/cli/skillCheck.d.ts +15 -1
- package/dist/cli/skillCheck.js +22 -1
- package/dist/cli/skillCheck.js.map +1 -1
- package/dist/cli/skillDirs.d.ts +5 -0
- package/dist/cli/skillDirs.js +13 -1
- package/dist/cli/skillDirs.js.map +1 -1
- package/dist/cli/skillSync.d.ts +4 -0
- package/dist/cli/skillSync.js +23 -0
- package/dist/cli/skillSync.js.map +1 -1
- package/dist/cli/sliceNew.d.ts +11 -7
- package/dist/cli/sliceNew.js +91 -18
- package/dist/cli/sliceNew.js.map +1 -1
- package/dist/cli/sliceSync.d.ts +50 -0
- package/dist/cli/sliceSync.js +170 -0
- package/dist/cli/sliceSync.js.map +1 -0
- package/dist/cli/status.d.ts +24 -1
- package/dist/cli/status.js +20 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/systemInputs.d.ts +53 -10
- package/dist/cli/systemInputs.js +186 -11
- package/dist/cli/systemInputs.js.map +1 -1
- package/dist/cli/upgrade.d.ts +16 -3
- package/dist/cli/upgrade.js +366 -30
- package/dist/cli/upgrade.js.map +1 -1
- package/dist/cli/usageLog.d.ts +4 -2
- package/dist/cli/usageLog.js +4 -2
- package/dist/cli/usageLog.js.map +1 -1
- package/dist/cli.js +599 -94
- package/dist/cli.js.map +1 -1
- package/dist/emit/api.d.ts +29 -0
- package/dist/emit/api.js +220 -0
- package/dist/emit/api.js.map +1 -0
- package/dist/emit/apiCheckJson.d.ts +3 -0
- package/dist/emit/apiCheckJson.js +21 -0
- package/dist/emit/apiCheckJson.js.map +1 -0
- package/dist/emit/codeownersJson.d.ts +3 -0
- package/dist/emit/codeownersJson.js +19 -0
- package/dist/emit/codeownersJson.js.map +1 -0
- 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/engagementJson.d.ts +8 -0
- package/dist/emit/engagementJson.js +46 -0
- package/dist/emit/engagementJson.js.map +1 -0
- package/dist/emit/glossaryJson.d.ts +1 -1
- package/dist/emit/glossaryJson.js +4 -1
- package/dist/emit/glossaryJson.js.map +1 -1
- package/dist/emit/json.d.ts +17 -1
- package/dist/emit/json.js +30 -1
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/metricsJson.d.ts +1 -1
- package/dist/emit/metricsJson.js +4 -1
- package/dist/emit/metricsJson.js.map +1 -1
- package/dist/emit/queryJson.d.ts +1 -1
- package/dist/emit/queryJson.js +7 -1
- package/dist/emit/queryJson.js.map +1 -1
- package/dist/emit/scopeJson.d.ts +12 -0
- package/dist/emit/scopeJson.js +26 -0
- package/dist/emit/scopeJson.js.map +1 -0
- package/dist/emit/skillCheckJson.d.ts +3 -2
- package/dist/emit/skillCheckJson.js +10 -2
- package/dist/emit/skillCheckJson.js.map +1 -1
- package/dist/emit/statusJson.d.ts +1 -1
- package/dist/emit/statusJson.js +15 -1
- package/dist/emit/statusJson.js.map +1 -1
- package/dist/emit/systemJson.d.ts +15 -8
- package/dist/emit/systemJson.js +28 -13
- package/dist/emit/systemJson.js.map +1 -1
- package/dist/emit/validateJson.d.ts +3 -3
- package/dist/emit/validateJson.js +10 -4
- package/dist/emit/validateJson.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +163 -31
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/diff.d.ts +4 -1
- package/dist/model/diff.js +44 -8
- package/dist/model/diff.js.map +1 -1
- package/dist/model/glossary.d.ts +2 -0
- package/dist/model/glossary.js +1 -0
- package/dist/model/glossary.js.map +1 -1
- package/dist/model/model.d.ts +55 -2
- package/dist/model/model.js +57 -0
- package/dist/model/model.js.map +1 -1
- package/dist/model/qualifiedRef.d.ts +18 -0
- package/dist/model/qualifiedRef.js +26 -0
- package/dist/model/qualifiedRef.js.map +1 -1
- package/dist/model/queryIndex.d.ts +9 -1
- package/dist/model/queryIndex.js +18 -2
- package/dist/model/queryIndex.js.map +1 -1
- package/dist/model/rules.d.ts +143 -2
- package/dist/model/rules.js +162 -3
- package/dist/model/rules.js.map +1 -1
- package/dist/model/validate.d.ts +40 -0
- package/dist/model/validate.js +147 -1
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +31 -3
- package/dist/parser/parser.js +148 -14
- package/dist/parser/parser.js.map +1 -1
- package/dist/query/format.js +1 -1
- package/dist/query/format.js.map +1 -1
- package/dist/query/verbs.d.ts +6 -0
- package/dist/query/verbs.js +6 -2
- package/dist/query/verbs.js.map +1 -1
- package/dist/system/adaptation.d.ts +14 -0
- package/dist/system/adaptation.js +49 -0
- package/dist/system/adaptation.js.map +1 -0
- package/dist/system/codeowners.d.ts +77 -0
- package/dist/system/codeowners.js +242 -0
- package/dist/system/codeowners.js.map +1 -0
- package/dist/system/manifest.d.ts +10 -5
- package/dist/system/manifest.js +54 -26
- package/dist/system/manifest.js.map +1 -1
- package/dist/system/migrateManifest.d.ts +28 -0
- package/dist/system/migrateManifest.js +155 -0
- package/dist/system/migrateManifest.js.map +1 -0
- package/dist/system/scope.d.ts +46 -0
- package/dist/system/scope.js +132 -0
- package/dist/system/scope.js.map +1 -0
- package/dist/system/verify.d.ts +50 -13
- package/dist/system/verify.js +179 -58
- package/dist/system/verify.js.map +1 -1
- package/dist/templates.d.ts +2 -2
- package/dist/templates.js +23 -2
- package/dist/templates.js.map +1 -1
- package/package.json +5 -2
- package/plugin/.claude-plugin/plugin.json +18 -0
- package/plugin/agents/em-critic.md +27 -0
- package/plugin/agents/em-implementer.md +35 -0
- package/plugin/agents/em-reviewer.md +32 -0
- package/plugin/agents/em-validator.md +28 -0
- package/plugin/shared/reference/em-dsl.md +871 -0
- package/plugin/shared/reference/methodology.md +329 -0
- package/plugin/shared/reference/operating-principles.md +222 -0
- package/plugin/shared/reference/slice-doc-schema.md +574 -0
- package/plugin/shared/templates/conformance-report.md +85 -0
- package/plugin/shared/templates/constitution.md +136 -0
- package/plugin/shared/templates/model-readme.md +46 -0
- package/plugin/shared/templates/slice.md +251 -0
- package/plugin/shared/templates/state.md +77 -0
- package/plugin/skills/conform/SKILL.md +93 -0
- package/plugin/skills/conform/reference/conform.md +428 -0
- package/plugin/skills/design/SKILL.md +298 -0
- package/plugin/skills/discover/SKILL.md +90 -0
- package/plugin/skills/discover/reference/extract.md +147 -0
- package/plugin/skills/engagement/SKILL.md +193 -0
- package/plugin/skills/engagement/reference/engagement.md +144 -0
- package/plugin/skills/event-modeling/SKILL.md +132 -0
- package/plugin/skills/implement/SKILL.md +61 -0
- package/plugin/skills/implement/reference/implement.md +439 -0
- package/plugin/skills/review/SKILL.md +74 -0
- package/dist/emit/typespec.d.ts +0 -26
- package/dist/emit/typespec.js +0 -356
- package/dist/emit/typespec.js.map +0 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design
|
|
3
|
+
em-version: 1.14.1
|
|
4
|
+
description: >-
|
|
5
|
+
Use when structuring a draft event model into swimlanes and the four patterns (State Change,
|
|
6
|
+
State View, Automation, Translation), evaluating a model's structural completeness, or writing
|
|
7
|
+
implementation-ready deep slice documents (field tables, invariants, Given/When/Then scenarios,
|
|
8
|
+
error flows) for a model that already has its happy-path spine. Drives `em`'s model and slice
|
|
9
|
+
phases — the core modeling work between discovery and implementation.
|
|
10
|
+
---
|
|
11
|
+
<!-- GENERATED FILE from .claude/skills/event-modeling-design/SKILL.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
|
|
12
|
+
|
|
13
|
+
# Event Modeling — model & slice
|
|
14
|
+
|
|
15
|
+
You are facilitating the core modeling work: turning a draft happy-path spine into a
|
|
16
|
+
structurally complete, validated model, and then a deep implementation-ready spec per slice.
|
|
17
|
+
Your job is to **extract an accurate model from the user through Socratic questioning** — never
|
|
18
|
+
to invent the domain. You drive the `em` CLI to render the model live, and you produce
|
|
19
|
+
implementation-ready slice docs.
|
|
20
|
+
|
|
21
|
+
Read `${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md` (preconditions, project
|
|
22
|
+
layout, the Socratic/validation discipline every phase follows) and
|
|
23
|
+
`${CLAUDE_PLUGIN_ROOT}/shared/reference/methodology.md` (the 7 steps + 4 patterns) before doing real
|
|
24
|
+
work — they are the source of truth. Run the preconditions there first: this phase expects a
|
|
25
|
+
draft model already exists (from `em:discover`'s `discover` or `extract` phase).
|
|
26
|
+
|
|
27
|
+
## Write scope, evidence, versions (MIL-236) — read before any edit
|
|
28
|
+
|
|
29
|
+
Full rules: `${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md` ("Write scope and
|
|
30
|
+
evidence"). In short:
|
|
31
|
+
|
|
32
|
+
- **Bound to this model's directory.** The session is bound to the model directory it started
|
|
33
|
+
in (`dirname(<model>.em)`). Never write under another model's directory or to its contract
|
|
34
|
+
file. Read a neighbour only through its public surface (`public: true` elements in
|
|
35
|
+
`em export <neighbour>.em`, or its committed contract), read-only.
|
|
36
|
+
- **Seam change request, never a cross-model edit.** When an open question needs another
|
|
37
|
+
model's `public` element changed, add one dated bullet to THIS model's `.event-modeling.md`
|
|
38
|
+
`## Decisions log`:
|
|
39
|
+
`- YYYY-MM-DD: seam change request → <producerKey>:<kind>.<slug> — <what> — <why>`.
|
|
40
|
+
Keep the question open, `blocked on:` the producer's owners. Never edit the other side.
|
|
41
|
+
- **Out of scope during design — never write:** generated contract artifacts
|
|
42
|
+
(`contracts/*.tsp`, any OpenAPI or JSON-schema output), SDD/spec artifacts (`specs/`,
|
|
43
|
+
`.specify/` — read the constitution, never write it), implementation source (`src/`, the
|
|
44
|
+
model's `Code roots:`), `plugin/`, and a draft slice doc's `version:`. Exception: after
|
|
45
|
+
marking an element `public` or changing a field of a `public` element, run
|
|
46
|
+
`em api generate <model>.em` and commit the regenerated contract with the model edit — the
|
|
47
|
+
API-first gate (`em validate --slice-ready`) refuses a public-touching slice whose contract
|
|
48
|
+
is missing or stale. Never hand-edit the contract.
|
|
49
|
+
- **Slice docs: edit authored sections only.** Never hand-edit a generated region
|
|
50
|
+
(`<!-- GENERATED:em-slice-…:start -->` … `:end -->`: field tables, the Invariants list); after
|
|
51
|
+
any model edit run `em slice sync <model>.em` (see **Generated vs authored sections** below).
|
|
52
|
+
- **Evidence rule.** The `.em` model and the slice docs are the evidence for modeling
|
|
53
|
+
decisions. Never read implementation source, generated contracts or `specs/` to decide a
|
|
54
|
+
modeling question — ask the user. Code is read only in `extract` and `conform`.
|
|
55
|
+
- **Version rule.** Never bump a draft's `version:` on edit. Before any `em slice reratify`,
|
|
56
|
+
run `em status <model>.em --json`: pre-release = the model's `modelVersion` entry has
|
|
57
|
+
`design: null` **and** `slices.byStatus.implemented === 0`. Pre-release → never run
|
|
58
|
+
`reratify`; edit `draft`/`reviewed` docs in place. Released → the re-ratification path in
|
|
59
|
+
the `slice` phase below.
|
|
60
|
+
- **Pre-commit check.** Stage the session's files, run `em system scope --staged` (STOP on any
|
|
61
|
+
`seam-crossing`), then `git diff --cached --name-only` (STOP on any path outside this model's
|
|
62
|
+
directory, any out-of-scope path above, or a changed `version:` line on a draft doc).
|
|
63
|
+
Unstage the offending path and tell the user why.
|
|
64
|
+
|
|
65
|
+
## Phase: `model` — steps 5-7
|
|
66
|
+
|
|
67
|
+
Goal: a structurally complete, **validated** model with correct patterns and swimlanes.
|
|
68
|
+
|
|
69
|
+
1. **Swimlanes & patterns (step 5).** Group events into **contexts** (bounded contexts /
|
|
70
|
+
aggregates) — ask which events share a consistency boundary and who owns them. Classify each
|
|
71
|
+
slice as one of the 4 patterns. **Share the slice between every automation or translation and
|
|
72
|
+
the command it triggers** (the reaction — processor/translation — together with the command
|
|
73
|
+
and event it produces; the read model it watches, if any, stays in the slice before). A
|
|
74
|
+
translation or automation is a reaction: it **triggers a command and never records an event
|
|
75
|
+
directly**. Add `translation` slices for external inputs (externally triggered: external →
|
|
76
|
+
translation → command → event, no slice before) and for the system reacting to its own state
|
|
77
|
+
(internally triggered: read model in the slice before → translation → command → event).
|
|
78
|
+
2. **Elaborate scenarios — first pass (step 6).** Scaffold every slice's doc now, one per
|
|
79
|
+
slice, none skipped: `em slice new "<slice name>" --pattern <state-change|state-view|
|
|
80
|
+
automation|translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes
|
|
81
|
+
`slices/<slice-name>.md` at `status: draft` (the same command, flags, and `--wire`
|
|
82
|
+
note-binding the `slice` phase's step 2 below documents in full). A slice whose only element
|
|
83
|
+
is a `view X again` instance is a **continuation** (MIL-208), not a slice to scaffold —
|
|
84
|
+
`--wire` refuses it and names the originating slice holding `X`'s first declaration; its
|
|
85
|
+
scenarios go into that doc instead. Then, for each freshly scaffolded doc, hold a first-pass
|
|
86
|
+
Socratic pass and write the happy-path Given/When/Then into
|
|
87
|
+
its `## Scenarios (Given / When / Then)` section (as `### Scenario:` blocks — see **Generated
|
|
88
|
+
vs authored sections** in the `slice` phase below) and the obvious invariants known so far into
|
|
89
|
+
its `## Invariants / Business Rules` section — **into the doc, never into the state file.**
|
|
90
|
+
Keep this pass shallow (no field tables, alternate/error flows, or NFRs yet — that's the full
|
|
91
|
+
deep-dive in the `slice` phase below); the docs are the home for everything collected from
|
|
92
|
+
here on, not yet implementation-ready specs. Because every doc already exists by the time the
|
|
93
|
+
`slice` phase begins, its own step 2 scaffolding call is then a no-op — that phase picks up
|
|
94
|
+
at its step 1's deeper pass. For a purely exploratory/backbone-mapping model that only needs
|
|
95
|
+
status coloring right now (not a full spec per slice yet), `em slice stub-all <model>.em` is
|
|
96
|
+
the one-command fast path: a near-free stub per undocumented slice, wired, in slice order
|
|
97
|
+
(MIL-184) — skip the per-slice `em slice new` calls above and deepen individual docs later
|
|
98
|
+
when the team is ready.
|
|
99
|
+
3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
|
|
100
|
+
half-slice — **every command has something that triggers it**, every command emits an event,
|
|
101
|
+
**every event is read by a read model**, **every read model has a consumer**, every view has a
|
|
102
|
+
source, every UI is reachable, every connection is
|
|
103
|
+
one of the six legal pairs, and every automation **and** translation triggers a command (none
|
|
104
|
+
wired straight to an event, none with no command at all). Run `em validate` and resolve all
|
|
105
|
+
errors and warnings — it catches a reaction wired straight to an event too now (see
|
|
106
|
+
`${CLAUDE_PLUGIN_ROOT}/shared/reference/em-dsl.md`), so a clean run covers this case.
|
|
107
|
+
For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
|
|
108
|
+
this fact and what they do with it. The honest answers are "here's the read model we missed" or
|
|
109
|
+
"nobody — so why are we recording it", and both improve the model.
|
|
110
|
+
**Per `view`, also ask who reads it (MIL-215):** a person or client reads it → draw the `ui`
|
|
111
|
+
that reads it; another model/system reads it → mark it `public`; only an automation reads
|
|
112
|
+
it → nothing, it's internal by design. No new keyword — reachability is already what `ui` and
|
|
113
|
+
`public` mean.
|
|
114
|
+
**Per translation fed from another model, declare what it consumes (MIL-235):** write
|
|
115
|
+
`translation Order Intake consumes checkout:event.order-submitted`. The ref is
|
|
116
|
+
`<modelKey>:<kind>.<slug>`, where kind is `event` or `view` and the target is marked `public`
|
|
117
|
+
in its own model. The consumer declares the seam; the producer never lists its consumers, and
|
|
118
|
+
the ref carries no version and no slice. Only a `translation` may carry `consumes`. If a
|
|
119
|
+
reaction reads another model, it is a translation. `em validate` checks only the ref's
|
|
120
|
+
grammar. Run `em system` from the repository (or on its `system.yaml`) to resolve it, and fix
|
|
121
|
+
any `consumes-unknown-model`/`consumes-unknown-element` error. Never edit the other model to
|
|
122
|
+
make a ref resolve. Ask its owner to publish the element instead, and record that ask as a
|
|
123
|
+
seam change request (see **Write scope** above).
|
|
124
|
+
**When you mark an element `public` here**, run `em api generate <model>.em` in the same
|
|
125
|
+
session and stage `contracts/<model key>.tsp` with the model edit — the one derived artifact
|
|
126
|
+
a design session produces, always by the command, never by hand.
|
|
127
|
+
|
|
128
|
+
End of phase: render, update state (`em state set-phase slice` — the mechanical marker the state
|
|
129
|
+
machine expects), and stop. Every slice already has a draft doc ready to receive the deep spec,
|
|
130
|
+
but slicing is deliberate work the team starts when it's ready for it, not this phase's automatic
|
|
131
|
+
next step.
|
|
132
|
+
|
|
133
|
+
## Phase: `slice` — deep slice documents
|
|
134
|
+
|
|
135
|
+
Goal: implementation-ready specs. Go slice by slice **in timeline order, starting with the first
|
|
136
|
+
slice on the storyboard** — never ask the user which slice to spec first, and never propose
|
|
137
|
+
starting with whichever slice looks highest-risk or most uncertain instead. Ratification
|
|
138
|
+
proceeds in the same order: later slices' scenarios and invariants depend on earlier slices'
|
|
139
|
+
events already being settled, so working out of order means re-litigating an earlier slice's
|
|
140
|
+
contract mid-spec. Check `README.md`'s Slices table for what's already done (run `em slice index
|
|
141
|
+
<model-name>.em` first if it looks stale).
|
|
142
|
+
|
|
143
|
+
This is also where **branch / unhappy-path events** are discovered and added to the model — as a
|
|
144
|
+
slice's alternate/error flows surface (a rejection, removal, cancellation, decline, expiry), add
|
|
145
|
+
the corresponding event/slice to the `.em` and re-render. The happy-path spine from earlier
|
|
146
|
+
phases is the starting point, not the finished event set. **Every new event needs its reader too**
|
|
147
|
+
— a rejection or cancellation that nothing projects will warn, and usually the missing piece is
|
|
148
|
+
real (someone has to see that the request was declined).
|
|
149
|
+
|
|
150
|
+
For each slice:
|
|
151
|
+
0. **Check for re-ratification first.** A `draft` or `reviewed` doc is never re-ratified: edit
|
|
152
|
+
it in place and leave its `version:` alone. If `slices/<slice-name>.md` already exists with
|
|
153
|
+
`status: implemented` (or is ratified and needs a change), run `em status <model>.em --json`
|
|
154
|
+
first. **Pre-release** (the model's `modelVersion` entry has `design: null` and
|
|
155
|
+
`slices.byStatus.implemented === 0`): never run `em slice reratify`; a ratified doc holds a
|
|
156
|
+
human sign-off, so STOP, park the change as an open question, and let the ratifier decide.
|
|
157
|
+
**Released** (either condition false): a doc with `status: implemented` is a
|
|
158
|
+
re-ratification, not fresh authoring: hold a Socratic
|
|
159
|
+
deep-dive on what changed (same rigor as step 1, scoped to the delta), update whichever
|
|
160
|
+
sections actually changed, then express the change as the `## Delta` section — fixed heading,
|
|
161
|
+
`### Added`/`Modified`/`Removed`/`Renamed` Requirement blocks each carrying its own scenarios
|
|
162
|
+
(see `${CLAUDE_PLUGIN_ROOT}/shared/templates/slice.md` and
|
|
163
|
+
`${CLAUDE_PLUGIN_ROOT}/shared/reference/slice-doc-schema.md#delta-section-grammar-and-lifecycle`)
|
|
164
|
+
— **overwriting** whatever the section held before, never appending to it. Then continue at
|
|
165
|
+
step 2 to bump `version` and flip `status` back to `ready-to-implement`, leaving
|
|
166
|
+
`implementedIn` naming the prior version's PR (intended drift signal, not a bug — see the
|
|
167
|
+
schema doc). Otherwise, this is first-time authoring: continue at step 1.
|
|
168
|
+
1. Hold a Socratic deep-dive to fill every section of `${CLAUDE_PLUGIN_ROOT}/shared/templates/slice.md`:
|
|
169
|
+
intent, trigger/actor,
|
|
170
|
+
command fields (types & rules), event(s) + payload (mark immutable facts) — field names and
|
|
171
|
+
types go into the `.em`, the doc's tables are generated from it — invariants
|
|
172
|
+
(give each a stable ID — see **Invariants live in the model** below), Given/When/Then scenarios (happy path + rule boundaries + edge cases),
|
|
173
|
+
alternate/error flows (retries, idempotency, compensations), non-functional requirements
|
|
174
|
+
(security/authz, PII/compliance, performance/SLA), read models affected, open questions. Park
|
|
175
|
+
anything unresolved rather than guessing. Before finalizing field names/types or invariants,
|
|
176
|
+
check whether the shape is already defined in **this model** (`.em` and its slice docs) or in
|
|
177
|
+
a neighbour's public surface (read-only) — don't guess a shape that's already defined there.
|
|
178
|
+
Never Grep or Read implementation source, OpenAPI files, DB migrations or `specs/` to settle
|
|
179
|
+
it (evidence rule): if the user says the existing code is the source of truth, ask them for
|
|
180
|
+
the shape, or park the question for an `extract` or `conform` session. Before finalizing an
|
|
181
|
+
invariant, also compare it with the slices in
|
|
182
|
+
**this same model** — never another `.em` model — that make the same kind of change (other
|
|
183
|
+
`Archive*` or `Create*` state changes on different entities, say). If one of them already
|
|
184
|
+
states an analogous rule, either state the same rule here — written out in this doc under
|
|
185
|
+
this slice's own `INV-<MNEMONIC>-n` ID, not by pointing at the sibling's — or record why this
|
|
186
|
+
slice's case differs. If this slice has a rule those siblings lack, raise that with the user
|
|
187
|
+
as an open question against each of them; a ratified sibling changes only through its own
|
|
188
|
+
re-ratification, never as a side effect of this slice. Repeating a rule across slice docs is
|
|
189
|
+
correct: each doc is a self-contained spec, and whether the code behind those rules is shared
|
|
190
|
+
is the constitution's call, not this doc's.
|
|
191
|
+
**Invariants live in the model.** Write every NEW invariant into the `.em` as a standalone
|
|
192
|
+
`invariant INV-<MNEMONIC>-<n> "rule sentence"` line directly after the command (or event) it
|
|
193
|
+
guards — always with the rule sentence — then cite that ID in the doc's Invariants section as a
|
|
194
|
+
plain mention and elaborate there (`- INV-CHK-1 — why, edge cases, the error returned`); never
|
|
195
|
+
restate it as a `**INV-CHK-1:**` rule (`em validate` flags that as
|
|
196
|
+
`invariants/declared-in-both`). A `public` command needs at least one invariant
|
|
197
|
+
(`invariants/public-command-without-invariants`). A doc that already declares its own
|
|
198
|
+
invariants keeps working; migrate one only when you are editing that slice anyway — move the
|
|
199
|
+
ID and rule into the model, keep the doc's elaboration. Run `em validate` after the edit:
|
|
200
|
+
`invariants/malformed-id` and `invariants/duplicate-id` are errors.
|
|
201
|
+
**Generated vs authored sections.** A doc `em slice new` wrote has two kinds of section. The
|
|
202
|
+
GENERATED regions — each command's, event's and read model's field table and the Invariants
|
|
203
|
+
list, between `<!-- GENERATED:em-slice-…:start -->` / `:end -->` markers — restate the model:
|
|
204
|
+
**never hand-edit a generated region.** To change a field, a type, or an invariant, edit the
|
|
205
|
+
`.em`, then **after any model edit run `em slice sync <model>.em`**, which rewrites every
|
|
206
|
+
region in place and leaves everything else byte-for-byte (`em slice sync <model>.em --check`
|
|
207
|
+
reports a stale doc without writing). **Edit authored sections only**: Intent, Trigger &
|
|
208
|
+
Actor, `Triggered by`/`Read by`/`Consumed by`, the invariant elaborations below the generated
|
|
209
|
+
list, Scenarios, Alternate & Error Flows, NFRs, Dependencies, Open Questions. Scenarios use the
|
|
210
|
+
constrained shape `em` reads back: a `### Scenario: <title>` heading, then `- **Given:**`,
|
|
211
|
+
`- **When:**`, `- **Then:**` bullets with nested ` - ` items — all three in every block
|
|
212
|
+
(`em validate` flags a block missing one as `slice-doc/structured-section-malformed`, and
|
|
213
|
+
`--slice-ready` refuses it). A doc written before em 1.14 has no markers; leave it as it is
|
|
214
|
+
unless you are re-creating it (`em slice new … --wire --force`, carrying the prose across).
|
|
215
|
+
A question this version will not answer is deferred with `em slice defer <model> <key> "<question>" --until v<n> --decision "<what this version does>"` — never left `- [ ]`, never deleted, never answered by guessing.
|
|
216
|
+
2. **First-time authoring:** scaffold the doc mechanically rather than hand-writing the
|
|
217
|
+
frontmatter — `em slice new "<slice name>" --pattern <state-change|state-view|automation|
|
|
218
|
+
translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes `slices/<slice-name>.md`
|
|
219
|
+
(kebab-cased to match, via the same slugging `em` uses everywhere else) with the canonical
|
|
220
|
+
`schemaVersion`/`pattern`/`swimlane`/`status: draft`/`version: 1` frontmatter — exactly the
|
|
221
|
+
five keys `em export` and the readiness gate require, no more — and the `# Slice:` heading +
|
|
222
|
+
diagram-image stub already in place. `--wire` also inserts the `note
|
|
223
|
+
"slices/<slice-name>.md"` line straight into the `.em`, onto the slice's primary element (the
|
|
224
|
+
command for State Change, the view for State View, the processor for Automation, the
|
|
225
|
+
translation for Translation), matched by export key — see step 4 for when it refuses instead.
|
|
226
|
+
A slice whose sole candidate view is a later `view X again` instance is a **continuation**
|
|
227
|
+
(MIL-208): `--wire` refuses it and names the originating slice holding `X`'s first
|
|
228
|
+
declaration — write this slice's scenarios into that doc instead of scaffolding a new one.
|
|
229
|
+
Both `--pattern` and `--swimlane` are required; an invalid `--pattern` is refused with the
|
|
230
|
+
valid choices listed. Never hand-type this block, and never fall back to a placeholder
|
|
231
|
+
pattern/swimlane to dodge the flags. It also writes every template section: the generated
|
|
232
|
+
regions already filled from the model (field tables, Invariants list), the authored sections
|
|
233
|
+
as placeholders. Then fill in the authored sections from step 1 (Intent, Trigger & Actor,
|
|
234
|
+
invariant elaborations, Scenarios, ...) — those stay hand-authored; the generated regions
|
|
235
|
+
follow the model through `em slice sync`. Record the originating need (ticket/conversation
|
|
236
|
+
link) in the Intent section when one exists. When this doc exists because of a split, merge, or rename, add the matching
|
|
237
|
+
lineage key(s) by hand (`split-from`/`merged-from`/`superseded-by`, `<slice-key>@v<N>` grammar
|
|
238
|
+
— see `${CLAUDE_PLUGIN_ROOT}/shared/reference/slice-doc-schema.md` for the full schema; `em validate` catches a malformed one
|
|
239
|
+
after the fact, see the `lineage-*` rules below).
|
|
240
|
+
**Re-ratification (step 0) — released models only.** Confirm the model is released first
|
|
241
|
+
(`em status <model>.em --json`: the `modelVersion` entry's `design` is not `null`, or
|
|
242
|
+
`slices.byStatus.implemented > 0`); on a pre-release model never run `reratify`. The doc
|
|
243
|
+
already exists, so `em slice new` doesn't apply here
|
|
244
|
+
(it refuses to overwrite an existing file without `--force`, and forcing would blow away the
|
|
245
|
+
doc's authored body) — run `em slice reratify <model>.em <slice-key>` instead: it bumps
|
|
246
|
+
`version` and flips `status` back to `ready-to-implement` in the existing frontmatter,
|
|
247
|
+
clearing any stale `ratifiedBy:`/`ratifiedOn:` from the prior version so a follow-up
|
|
248
|
+
`em slice ratify --by <name>` (if the team records that) applies cleanly. The same command
|
|
249
|
+
changes a ratified doc that **never shipped** (`ready-to-implement` with `ratifiedBy` set — a
|
|
250
|
+
gap answered mid-build, MIL-258): it bumps `version` and clears the sign-off but leaves
|
|
251
|
+
`status` alone. Either way the doc is not ratified until `em slice ratify --by <name>` records
|
|
252
|
+
the new sign-off. A `ready-to-implement` doc with no `ratifiedBy` is already awaiting that
|
|
253
|
+
sign-off and refuses a second `reratify`; a `draft`/`reviewed` doc is simply edited, and its
|
|
254
|
+
`version:` never moves.
|
|
255
|
+
**Public-touching slices (MIL-238).** On a slice that owns a `public` command, event or view,
|
|
256
|
+
`em slice ratify --by <name>` and `em slice reratify` refuse without one of two flags: pass
|
|
257
|
+
`--meaning-unchanged` when this version does not change what the public contract means, or
|
|
258
|
+
`--contract-change "<why>"` when a consumer must read the change differently. Without
|
|
259
|
+
either, both refuse with `slice "<key>" touches the public surface — pass
|
|
260
|
+
--meaning-unchanged, or --contract-change "<why>" if a consumer must read this change
|
|
261
|
+
differently`; passing both refuses too. Run
|
|
262
|
+
`em api generate <model>.em` first, so `em validate --slice-ready` sees a current contract
|
|
263
|
+
(otherwise it blocks with `slice-ready-contract-stale`).
|
|
264
|
+
Which flag applies is the ratifier's call, not yours: ask, never pick one to make the
|
|
265
|
+
command pass. Ratification itself stays a human gate outside this session; a review session
|
|
266
|
+
never ratifies (see `em:review`).
|
|
267
|
+
3. Render the slice's own diagram: `em render <model>.em --slice "<slice name>" -o
|
|
268
|
+
slices/<slice-name>.svg` (kebab-case, matching the doc's filename and the `![Diagram]` stub
|
|
269
|
+
`em slice new` already wrote) — redraws just this slice in its own canonical pattern shape.
|
|
270
|
+
4. Confirm it's wired into the `.em`: `em slice new --wire` (step 2) already inserted the `note
|
|
271
|
+
"slices/<slice-name>.md"` line onto the slice's primary element's own declaration line — it
|
|
272
|
+
only ever edits that one line, and only when doing so is unambiguous, so it refuses (writing
|
|
273
|
+
NEITHER the doc nor the `.em` edit) rather than guess when the slice has zero or more than one
|
|
274
|
+
candidate element of the primary kind, or when that line already carries a `note` clause. On a
|
|
275
|
+
refusal, re-run step 2 without `--wire` — it prints the exact line to add, paste it onto the
|
|
276
|
+
slice's primary element by hand.
|
|
277
|
+
5. Run `em slice index <model-name>.em` to regenerate `README.md`'s Slices table — the one
|
|
278
|
+
canonical slice index — from the model and the doc frontmatter you just wrote (status,
|
|
279
|
+
`implementedIn` once shipped). Never hand-edit the table; it's a generated block.
|
|
280
|
+
6. Re-render, run `em slice sync <model-name>.em --check` (it must print `ok:` for every doc you
|
|
281
|
+
touched), and `em validate`.
|
|
282
|
+
7. Before committing, stage the session's files and run the pre-commit check: `em system scope
|
|
283
|
+
--staged`, then `git diff --cached --name-only`. STOP on any `seam-crossing`, any path outside
|
|
284
|
+
this model's directory, any out-of-scope path (see **Write scope** above), or a changed
|
|
285
|
+
`version:` line on a draft doc.
|
|
286
|
+
|
|
287
|
+
A finished draft doesn't become implementable here: it goes through the review gate and then the
|
|
288
|
+
ratification gate, both human, both outside this phase — see
|
|
289
|
+
`docs/process.md#the-slice-lifecycle-gates` for who runs which and in what order.
|
|
290
|
+
|
|
291
|
+
Before the project's first slice is implemented it also needs a ratified implementation
|
|
292
|
+
constitution (stack, code style, testing norms, NFR baselines, review norms) — the
|
|
293
|
+
`em:implement` skill elicits it, see its `reference/implement.md` §7.
|
|
294
|
+
|
|
295
|
+
End of phase: every slice that should exist has a doc — except a continuation slice (MIL-208, a
|
|
296
|
+
later `view X again` instance), which has none by design and is documented by the originating
|
|
297
|
+
slice instead — `README.md`'s Slices table is current, and the model validates clean. Suggest
|
|
298
|
+
`em:implement` for each ratified slice.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: discover
|
|
3
|
+
em-version: 1.14.1
|
|
4
|
+
description: >-
|
|
5
|
+
Use when starting a brand-new event model from scratch — greenfield discovery: brainstorming
|
|
6
|
+
past-tense events, storyboarding the happy path, finding the commands and read models — or when
|
|
7
|
+
extracting a current-state model from an EXISTING codebase or system (event-driven or
|
|
8
|
+
procedural). Drives `em`'s discover and extract phases; produces a draft or as-is `.em` model
|
|
9
|
+
ready for the model/slice phases (`em:design`).
|
|
10
|
+
---
|
|
11
|
+
<!-- GENERATED FILE from .claude/skills/event-modeling-discover/SKILL.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
|
|
12
|
+
|
|
13
|
+
# Event Modeling — discover & extract
|
|
14
|
+
|
|
15
|
+
You are facilitating the start of an Event Modeling session. Your job is to **extract an
|
|
16
|
+
accurate model from the user through Socratic questioning** — never to invent the domain. You
|
|
17
|
+
drive the `em` CLI to render the model live.
|
|
18
|
+
|
|
19
|
+
Read `${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md` (preconditions, project
|
|
20
|
+
layout, the Socratic/validation discipline every phase follows) and
|
|
21
|
+
`${CLAUDE_PLUGIN_ROOT}/shared/reference/methodology.md` (the 7 steps + 4 patterns) before doing real
|
|
22
|
+
work — they are the source of truth. Run the preconditions there first.
|
|
23
|
+
|
|
24
|
+
**Existing system?** Use the `extract` phase below. **New feature inside an existing codebase,
|
|
25
|
+
not a whole-system extraction?** Stay in `discover`, but before locking in command/event names,
|
|
26
|
+
Grep/Read adjacent real sources (OpenAPI specs, DB migrations, existing DTOs/event classes in
|
|
27
|
+
sibling contexts) rather than guessing conventions — same check the `slice` phase applies to
|
|
28
|
+
field tables (`em:design`).
|
|
29
|
+
|
|
30
|
+
## Phase: `discover` — steps 1-4
|
|
31
|
+
|
|
32
|
+
Goal: a draft model of events, storyboard, commands, and views. Loose is OK; structure comes next.
|
|
33
|
+
|
|
34
|
+
1. **Brainstorm events (step 1).** Ask the user to name everything that happens, as past-tense
|
|
35
|
+
facts. Probe for missed state changes. For each candidate, apply the **"is it an event?"
|
|
36
|
+
test** (`methodology.md` step 1 — the "would this wake the CEO at 3am" heuristic):
|
|
37
|
+
reject derived values (belong in a read model) and telemetry/activity (not business state).
|
|
38
|
+
Don't silently drop rejections — park them in the state file's Decisions log with why, so
|
|
39
|
+
they aren't re-proposed later. Capture the survivors as a flat list.
|
|
40
|
+
2. **Plot / storyboard (step 2).** Order the events into the narrative. Identify **personas**
|
|
41
|
+
(actors) and the **UI** screens at each step. Add `persona` declarations and `ui` elements.
|
|
42
|
+
In a **headless/API** model there are no human personas or screens — identify the external
|
|
43
|
+
**callers** at each step instead; declare one persona per caller/role, and its `ui` boxes are
|
|
44
|
+
the API calls (see `methodology.md`).
|
|
45
|
+
3. **Inputs (step 3).** For each event, find the **command** that causes it (imperative name).
|
|
46
|
+
Form `command → event` slices (State Change pattern). Every command needs a **trigger** in the
|
|
47
|
+
same breath: the screen the user issues it from (a `ui` in the slice), or — for the Automation
|
|
48
|
+
and Translation patterns — the reaction that triggers it, also in this slice. A command
|
|
49
|
+
nothing points at is a write nobody can start. *"Who does this, and where are they when they
|
|
50
|
+
do it?"*
|
|
51
|
+
4. **Outputs (step 4).** Identify the **read models / views** consumers need and wire them with
|
|
52
|
+
`from "Event"` (State View pattern), each with the screen (or reaction) that consumes it — a
|
|
53
|
+
read model nothing displays is information dropped on the floor. In a **headless/API** model
|
|
54
|
+
the consumer is the caller persona's `ui` (the API query) — same as any screen. Read models are
|
|
55
|
+
**repeated** in each slice where they're read (use `view X again` after the first, each instance
|
|
56
|
+
with its own consumer) so the timeline flows (see `methodology.md`).
|
|
57
|
+
**Close the loop before leaving this step:** every event from step 3 must be read by at least one
|
|
58
|
+
read model. Walk the event list and ask, for each one, *"who looks at this, and what do they do
|
|
59
|
+
with it?"* An event with no answer is either missing its read model or shouldn't be recorded —
|
|
60
|
+
both are worth raising with the user rather than leaving dangling.
|
|
61
|
+
|
|
62
|
+
End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (steps done,
|
|
63
|
+
decisions, open questions — run `em state set-phase model` rather than hand-editing `Current
|
|
64
|
+
phase:`), and run `em slice index <model-name>.em` to seed `README.md`'s Slices table (every
|
|
65
|
+
slice already reads "no doc yet" — no slice docs exist until the `slice` phase writes them).
|
|
66
|
+
Tell the user they can stop here and resume with `/em:event-modeling` (or invoke
|
|
67
|
+
`em:design` directly).
|
|
68
|
+
|
|
69
|
+
## Phase: `extract` — current-state model of an existing system
|
|
70
|
+
|
|
71
|
+
Goal: a faithful **current-state** model extracted from an existing system — the as-is sibling
|
|
72
|
+
of `discover` (it replaces discover; the model then proceeds to `model` as usual, in
|
|
73
|
+
`em:design`).
|
|
74
|
+
|
|
75
|
+
**Read `reference/extract.md` before doing any extract work** — it carries a stance override
|
|
76
|
+
that inverts methodology step 1 (capture how the system behaves *today*; never model desired
|
|
77
|
+
state).
|
|
78
|
+
|
|
79
|
+
- Two source modes, detected from the system itself and confirmed with the user:
|
|
80
|
+
**event-driven** (an event vocabulary already exists — schemas, topics, handlers) and
|
|
81
|
+
**procedural/monolith** (no event vocabulary — synthesize candidate events from behavior
|
|
82
|
+
and docs).
|
|
83
|
+
- Converge via a ~7-round confirm-and-clarify loop (one concern per round; render from round 2,
|
|
84
|
+
validate from round 4 — see the playbook).
|
|
85
|
+
- **Current-state-only:** park unknowns as `# TBD` comments in the `.em`, mirrored in the state
|
|
86
|
+
file's Open Questions — never guess intended design.
|
|
87
|
+
|
|
88
|
+
End of phase: validated as-is model; state file updated (source mode, rounds, decisions; `em
|
|
89
|
+
state set-phase model` for `Current phase:`); run `em slice index <model-name>.em` to seed
|
|
90
|
+
`README.md`'s Slices table; then hand off to `em:design` (steps 5-7).
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<!-- GENERATED FILE from .claude/skills/event-modeling-discover/reference/extract.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
|
|
2
|
+
# Extract Phase — Current-State Model of an Existing System
|
|
3
|
+
|
|
4
|
+
Read this **before doing any extract work**. It governs the `extract` phase and **overrides
|
|
5
|
+
`methodology.md` step 1's stance** for the duration of extraction: you are capturing how the
|
|
6
|
+
system behaves **today**, not modeling what the business needs. Everything else in the
|
|
7
|
+
methodology — the 4 patterns, the "is it an event?" test, the Socratic stance, the
|
|
8
|
+
reaction-shares-a-slice-with-its-command shape — still applies.
|
|
9
|
+
|
|
10
|
+
## Stance override — current-state-only
|
|
11
|
+
|
|
12
|
+
- Capture the system **as it is**, faithfully, warts included. Never invent future or desired
|
|
13
|
+
state. Improvement ideas that surface go to the state file's Open Questions or Decisions log
|
|
14
|
+
— never into the model.
|
|
15
|
+
- Methodology step 1's caution ("model the process the business **needs**, not how the current
|
|
16
|
+
system happens to work — existing system behavior is a common source of fake events") is
|
|
17
|
+
**deliberately suspended**: here the existing system *is* the subject.
|
|
18
|
+
- The **"is it an event?" filter still applies.** Derived values (totals, calculated fields)
|
|
19
|
+
and telemetry/activity (clicks, page views, logins) are still not events, even when the
|
|
20
|
+
current system stores or emits them as such. Park each rejection in the Decisions log with
|
|
21
|
+
why, so it isn't re-proposed.
|
|
22
|
+
- **Never guess.** Unknown or ambiguous current behavior is parked (see TBD convention below),
|
|
23
|
+
not filled in with what the design "probably" intends.
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
|
|
27
|
+
1. Run the shared preconditions (tool check, model location) — see
|
|
28
|
+
`../../../shared/reference/operating-principles.md`. If no model exists yet, run
|
|
29
|
+
`em scaffold <model-name>` (see that doc's "Project layout this skill creates").
|
|
30
|
+
2. **Agree the scope line first**: which system, which part, what's out of bounds. Write it to
|
|
31
|
+
the state file's Session inputs.
|
|
32
|
+
3. Record in the state file: `Source mode` (after detection, below) and
|
|
33
|
+
`Existing system refs` — the repo paths, event-schema/topic locations, and docs you will
|
|
34
|
+
extract from.
|
|
35
|
+
|
|
36
|
+
## Mode detection
|
|
37
|
+
|
|
38
|
+
Inspect the system with your own tools (Grep/Read over the codebase and docs), then confirm
|
|
39
|
+
the mode with the user:
|
|
40
|
+
|
|
41
|
+
- **Event-driven** — the system already has an event vocabulary. Signals: event
|
|
42
|
+
schemas/classes, topics/streams/queues, message or CDC handlers, an event store, pub/sub
|
|
43
|
+
wiring. Extraction *lifts* the model from that vocabulary.
|
|
44
|
+
- **Procedural / monolith** — no event vocabulary. Signals: transaction scripts, service
|
|
45
|
+
methods mutating rows, CRUD controllers, procedural runbooks/docs. Extraction *synthesizes*
|
|
46
|
+
candidate events from the state changes the behavior implies.
|
|
47
|
+
|
|
48
|
+
Mixed systems happen: pick the dominant mode, note the exception areas in the state file.
|
|
49
|
+
|
|
50
|
+
## Sourcing by mode
|
|
51
|
+
|
|
52
|
+
- **Event-driven:** inventory the emitted/consumed event types; take past-tense business facts
|
|
53
|
+
directly; then filter — technical/infrastructure messages (retries, heartbeats, envelope
|
|
54
|
+
wrappers) and derived/telemetry types are dropped (log why). Keep the system's own names
|
|
55
|
+
where they are honest past-tense facts; propose a cleaner past-tense name only when the
|
|
56
|
+
original is technical (record the mapping in the Decisions log).
|
|
57
|
+
- **Procedural:** for each significant operation, ask "what business fact has become true when
|
|
58
|
+
this completes?" — those are the candidate events. Screens and tables are *evidence* of
|
|
59
|
+
state changes, not events themselves; a table row's lifecycle (created/updated/closed) often
|
|
60
|
+
implies several events. Apply the event filter hard here — procedural systems are where
|
|
61
|
+
derived values and activity records masquerade as facts.
|
|
62
|
+
|
|
63
|
+
Before minting a new candidate event/command/view name, check whether sibling `.em` models
|
|
64
|
+
already exist in the repo. If they do, run `em glossary <this-model>.em <sibling>.em ...` (or
|
|
65
|
+
`--json` it) as a fuzzy-name-matching aid: it surfaces the same normalized term used with a
|
|
66
|
+
different kind or a different field type across models. A hit there usually means the
|
|
67
|
+
extracted system already has an established name/kind elsewhere — prefer that over minting a
|
|
68
|
+
near-duplicate synonym (e.g. "Order Confirmed" vs. an existing "Order Confirmation"). This is
|
|
69
|
+
informational judgment, not a gate: `em glossary` only flags conflicts, it doesn't resolve
|
|
70
|
+
them.
|
|
71
|
+
|
|
72
|
+
## The confirm-and-clarify loop (~7 rounds)
|
|
73
|
+
|
|
74
|
+
One concern per round. After every round: mirror what you extracted back to the user, update
|
|
75
|
+
the `.em`, and (from R2) re-render so they see the model grow. Convergence beats coverage —
|
|
76
|
+
it is fine to finish a round with `# TBD`s still parked.
|
|
77
|
+
|
|
78
|
+
- **R1 — Candidate events.** Extract (event-driven) or synthesize (procedural) the past-tense
|
|
79
|
+
facts; apply the event test; user confirms/renames/rejects. Ambiguous candidates → `# TBD`.
|
|
80
|
+
- **R2 — Timeline order.** Order the confirmed events into the as-is narrative. Identify the
|
|
81
|
+
*actual* actors/callers and the *real* UI or API surfaces (headless systems: still `ui`/`persona`
|
|
82
|
+
— one persona per caller/role, its `ui` boxes are the API calls, per `em-dsl.md`). **First
|
|
83
|
+
`em render` here.**
|
|
84
|
+
- **R3 — Commands / inputs.** For each event, the request that actually produces it today
|
|
85
|
+
(imperative name), *and what triggers it* — the real screen, or the scheduler/listener that
|
|
86
|
+
fires it (model that as a reaction sharing this same slice, per R5). Unclear trigger → `# TBD`,
|
|
87
|
+
not a plausible guess; expect a *command has nothing that triggers it* warning until R5 fills
|
|
88
|
+
it in, and don't invent a screen to silence it.
|
|
89
|
+
- **R4 — Read models / outputs.** The projections/queries the system actually serves; wire
|
|
90
|
+
`from "Event"` (repeat read models span-1 per `em-dsl.md`, using `view X again` after the
|
|
91
|
+
first). **Start running `em validate`** each round from here. Expect *event is not read by any
|
|
92
|
+
read model* warnings: real systems do record fire-and-forget events. In an as-is model that is
|
|
93
|
+
a **finding about the system**, not a modelling mistake — never invent a reader to silence it
|
|
94
|
+
(that would be desired state). Park it in the Decisions log and carry the warning.
|
|
95
|
+
- **R5 — Boundaries & reactions.** Current automations and integrations as-is: schedulers,
|
|
96
|
+
listeners, outbound syncs, inbound webhooks. Model each as a reaction sharing its slice with
|
|
97
|
+
the `command` + `event` it triggers (`reaction → command → event`, per `em-dsl.md`) — the
|
|
98
|
+
read model it watches, if any, lives in the slice before, named via the reaction's own
|
|
99
|
+
`from`. This matches how the current code usually already reads (scheduler/listener → one
|
|
100
|
+
procedural step); note any real as-built difference in the slice's `# TBD`/Decisions.
|
|
101
|
+
- **R6 — Gap & TBD reconciliation.** Walk every parked `# TBD` with the user. Resolve only
|
|
102
|
+
what they can confirm **as a current-state fact**; everything else stays parked. Explicitly
|
|
103
|
+
refuse to resolve a TBD by inventing desired behavior.
|
|
104
|
+
- **R7 — Convergence.** Final `em render` + `em validate` (fix errors and warnings — this now
|
|
105
|
+
catches a reaction wired straight to an event too, via *reaction triggers no command*). User
|
|
106
|
+
confirms the model is a faithful picture of today. Mark extraction complete in the state file
|
|
107
|
+
and hand off.
|
|
108
|
+
|
|
109
|
+
Seven rounds is the default shape, not a quota — collapse rounds for a small system, repeat
|
|
110
|
+
one for a sprawling one. Record any deviation in the Decisions log.
|
|
111
|
+
|
|
112
|
+
## TBD parking convention
|
|
113
|
+
|
|
114
|
+
- Inline `.em` comment on its own line directly above (or trailing) the element it concerns:
|
|
115
|
+
`# TBD: <specific question about current behavior>`. Comments are native DSL syntax — they
|
|
116
|
+
render and validate cleanly.
|
|
117
|
+
- Mirror every `# TBD` 1:1 as a checkbox in the state file's **Open questions / parking lot** —
|
|
118
|
+
add `source`/`blocked on`/`revisit` metadata when known — and remove both together when
|
|
119
|
+
resolved.
|
|
120
|
+
- A `# TBD` is only ever resolved by a confirmed current-state fact — never replaced by a
|
|
121
|
+
guessed element.
|
|
122
|
+
|
|
123
|
+
## Completion & handoff
|
|
124
|
+
|
|
125
|
+
Extraction is complete when: the model renders; `em validate` is clean (or remaining warnings
|
|
126
|
+
are consciously accepted in the Decisions log — unread events in an as-is model are the usual
|
|
127
|
+
accepted case, and each one is worth naming explicitly as a finding); every unknown is a parked
|
|
128
|
+
`# TBD`; and the user has confirmed the model reflects today's behavior. Then:
|
|
129
|
+
|
|
130
|
+
1. State file: set phase progress (extraction rounds checked), log the completion decision.
|
|
131
|
+
Run `em slice index <model-name>.em` to seed `README.md`'s **Slices table** — every slice,
|
|
132
|
+
its AST-derived pattern, status "no doc yet" (no slice docs exist until the `slice` phase
|
|
133
|
+
writes them).
|
|
134
|
+
2. The extracted model **replaces `discover`** — suggest `/em:event-modeling model` next
|
|
135
|
+
(steps 5-7: swimlanes, patterns, completeness). Desired-state changes belong *after* the
|
|
136
|
+
as-is model is agreed — as normal `model`/`slice` evolution, clearly separated from
|
|
137
|
+
extraction.
|
|
138
|
+
|
|
139
|
+
## Anti-patterns
|
|
140
|
+
|
|
141
|
+
- **Inventing desired state** — the cardinal sin of extraction; park it instead.
|
|
142
|
+
- **Importing screens/tables as events** — they are evidence of state changes, not facts.
|
|
143
|
+
- **Guessing an unclear trigger or reaction** instead of parking a `# TBD`.
|
|
144
|
+
- **Cleaning up the domain while extracting** — renames and restructures beyond honest
|
|
145
|
+
past-tense naming are `model`-phase work, done with the user after the as-is picture exists.
|
|
146
|
+
- **Skipping render/validate until the end** — the loop depends on the user seeing the model
|
|
147
|
+
grow round by round.
|