@milehimikey/em 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/event-modeling/SKILL.md +66 -16
- package/.claude/skills/event-modeling/reference/conform.md +228 -0
- package/.claude/skills/event-modeling/reference/em-dsl.md +22 -5
- package/.claude/skills/event-modeling/reference/extract.md +5 -4
- package/.claude/skills/event-modeling/reference/methodology.md +11 -3
- package/.claude/skills/event-modeling/templates/conformance-report.md +69 -0
- package/.claude/skills/event-modeling/templates/model-readme.md +6 -4
- package/.claude/skills/event-modeling/templates/slice.md +11 -2
- package/.claude/skills/event-modeling/templates/state.md +17 -7
- package/README.md +17 -3
- package/dist/cli/changelog-git.d.ts +53 -0
- package/dist/cli/changelog-git.js +112 -0
- package/dist/cli/changelog-git.js.map +1 -0
- package/dist/cli/diff-inputs.d.ts +44 -0
- package/dist/cli/diff-inputs.js +62 -0
- package/dist/cli/diff-inputs.js.map +1 -0
- package/dist/cli.js +231 -7
- package/dist/cli.js.map +1 -1
- package/dist/emit/changelog.d.ts +57 -0
- package/dist/emit/changelog.js +143 -0
- package/dist/emit/changelog.js.map +1 -0
- package/dist/emit/diffJson.d.ts +17 -0
- package/dist/emit/diffJson.js +74 -0
- package/dist/emit/diffJson.js.map +1 -0
- package/dist/emit/json.d.ts +29 -0
- package/dist/emit/json.js +121 -0
- package/dist/emit/json.js.map +1 -0
- package/dist/model/diff.d.ts +52 -0
- package/dist/model/diff.js +351 -0
- package/dist/model/diff.js.map +1 -0
- package/dist/model/model.d.ts +2 -0
- package/dist/model/model.js +1 -0
- package/dist/model/model.js.map +1 -1
- package/dist/model/validate.js +74 -0
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +2 -0
- package/dist/parser/parser.js +8 -0
- package/dist/parser/parser.js.map +1 -1
- package/dist/render/drawNotes.d.ts +8 -2
- package/dist/render/drawNotes.js +106 -42
- package/dist/render/drawNotes.js.map +1 -1
- package/dist/render/render.js +5 -4
- package/dist/render/render.js.map +1 -1
- package/package.json +3 -2
|
@@ -5,11 +5,13 @@ 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, run the em tool,
|
|
9
|
-
event model from an existing system or codebase (event-driven or legacy/procedural)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
`
|
|
8
|
+
build or edit an `.em` model, run the em tool, extract / reverse-engineer a current-state
|
|
9
|
+
event model from an existing system or codebase (event-driven or legacy/procedural), or check
|
|
10
|
+
a ratified model for drift against the codebase that implements it. Works in resumable phases
|
|
11
|
+
via an argument: `discover` (steps 1-4, greenfield), `extract` (current-state model of an
|
|
12
|
+
existing system), `model` (steps 5-7), `slice` (deep slice specs), `conform` (drift check
|
|
13
|
+
against the codebase), plus `watch` and `validate`. With no argument it resumes from the saved
|
|
14
|
+
state file.
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
# Event Modeling with `em`
|
|
@@ -28,7 +30,8 @@ validation rules) before doing real work — they are the source of truth. Templ
|
|
|
28
30
|
multiple-choice decisions. Never assume a domain fact — extract it. Prefer "who/why/what-if/
|
|
29
31
|
what-must-always-be-true/how-do-you-know" over yes-no.
|
|
30
32
|
- **Don't guess — park it.** Unresolved items go into the Open Questions list in
|
|
31
|
-
`.event-modeling.md`, not into invented model content.
|
|
33
|
+
`.event-modeling.md`, not into invented model content. Note the source, blocker, and revisit
|
|
34
|
+
date when known — cheap now, useful when you come back to it.
|
|
32
35
|
- **Happy path first; branches belong to slicing.** Steps 1-7 build the **happy-path spine**.
|
|
33
36
|
Alternate, unhappy, and exception-path events (rejections, removals, cancellations,
|
|
34
37
|
expirations, declines) are **not** enumerated as a separate discover/model task — they
|
|
@@ -63,6 +66,9 @@ validation rules) before doing real work — they are the source of truth. Templ
|
|
|
63
66
|
3. Parse the argument (`$ARGUMENTS`) to pick the phase below. With **no argument**, read the
|
|
64
67
|
state file and resume the recorded phase/step; if no model exists, propose starting `discover`
|
|
65
68
|
(greenfield) or `extract` (modeling an existing system).
|
|
69
|
+
4. Populate the state file's Participants section at session start. For a live workshop, ask for
|
|
70
|
+
a single human proxy to relay questions to the room, and attribute every answer/decision in
|
|
71
|
+
the Decisions log to a named participant.
|
|
66
72
|
|
|
67
73
|
## Project layout this skill creates
|
|
68
74
|
|
|
@@ -74,13 +80,19 @@ validation rules) before doing real work — they are the source of truth. Templ
|
|
|
74
80
|
README.md # overview + slice index (from templates/model-readme.md)
|
|
75
81
|
.event-modeling.md # resumable state (from templates/state.md)
|
|
76
82
|
slices/<slice-name>.md # one rich slice doc per slice (from templates/slice.md)
|
|
83
|
+
conformance/<date>-report.md
|
|
84
|
+
# conform-phase reports (from templates/conformance-report.md)
|
|
85
|
+
<model-name>-asis.em # conform-phase scratch model, regenerated per run — git-ignore this
|
|
77
86
|
```
|
|
78
87
|
|
|
79
88
|
When creating a new model, scaffold the directory and copy the templates in (filling the
|
|
80
89
|
placeholders). Copy `live.html` **verbatim** — it takes the SVG name from its URL query
|
|
81
90
|
(`live.html?svg=<model-name>.svg`), so there's nothing to edit. You may use `em init` for a
|
|
82
91
|
starter `.em`, but usually you'll build it up from the discovery conversation instead. Fill
|
|
83
|
-
template placeholders — never leave `{{...}}` in delivered files.
|
|
92
|
+
template placeholders — never leave `{{...}}` in delivered files. The `conformance/` directory
|
|
93
|
+
and `<model-name>-asis.em` only appear once the `conform` phase runs; add the pattern `*-asis.em`
|
|
94
|
+
to the repository's `.gitignore` the first time one is created (see `reference/conform.md`) —
|
|
95
|
+
it's scratch, never committed.
|
|
84
96
|
|
|
85
97
|
---
|
|
86
98
|
|
|
@@ -88,6 +100,10 @@ template placeholders — never leave `{{...}}` in delivered files.
|
|
|
88
100
|
|
|
89
101
|
Goal: a draft model of events, storyboard, commands, and views. Loose is OK; structure comes next.
|
|
90
102
|
**Existing system?** Use `extract` instead (next section) — discover is for greenfield modeling.
|
|
103
|
+
**New feature inside an existing codebase?** Stay in `discover`, but before locking in
|
|
104
|
+
command/event names, Grep/Read adjacent real sources (OpenAPI specs, DB migrations, existing
|
|
105
|
+
DTOs/event classes in sibling contexts) rather than guessing conventions — same check `slice`
|
|
106
|
+
phase applies to field tables, below.
|
|
91
107
|
|
|
92
108
|
1. **Brainstorm events (step 1).** Ask the user to name everything that happens, as past-tense
|
|
93
109
|
facts. Probe for missed state changes. For each candidate, apply the **"is it an event?"
|
|
@@ -109,8 +125,8 @@ Goal: a draft model of events, storyboard, commands, and views. Loose is OK; str
|
|
|
109
125
|
`reference/methodology.md`).
|
|
110
126
|
|
|
111
127
|
End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (steps done,
|
|
112
|
-
decisions, open questions
|
|
113
|
-
`/event-modeling model`.
|
|
128
|
+
decisions, open questions) and `README.md`'s slice index. Tell the user they can stop here and
|
|
129
|
+
resume with `/event-modeling model`.
|
|
114
130
|
|
|
115
131
|
## Phase: `extract` — current-state model of an existing system
|
|
116
132
|
|
|
@@ -130,8 +146,8 @@ state).
|
|
|
130
146
|
- **Current-state-only:** park unknowns as `# TBD` comments in the `.em`, mirrored in the state
|
|
131
147
|
file's Open Questions — never guess intended design.
|
|
132
148
|
|
|
133
|
-
End of phase: validated as-is model; state file updated (source mode, rounds, decisions
|
|
134
|
-
|
|
149
|
+
End of phase: validated as-is model; state file updated (source mode, rounds, decisions) and
|
|
150
|
+
`README.md`'s slice index seeded; then chain to `/event-modeling model` (steps 5-7).
|
|
135
151
|
|
|
136
152
|
## Phase: `model` — steps 5-7
|
|
137
153
|
|
|
@@ -160,7 +176,7 @@ End of phase: render, update state, suggest `/event-modeling slice` to write imp
|
|
|
160
176
|
## Phase: `slice` — deep slice documents
|
|
161
177
|
|
|
162
178
|
Goal: implementation-ready specs. Go slice by slice (let the user pick order, or follow the
|
|
163
|
-
timeline). Check
|
|
179
|
+
timeline). Check `README.md`'s slice index for what's already done.
|
|
164
180
|
|
|
165
181
|
This is also where **branch / unhappy-path events** are discovered and added to the model — as a
|
|
166
182
|
slice's alternate/error flows surface (a rejection, removal, cancellation, decline, expiry), add
|
|
@@ -171,15 +187,44 @@ For each slice:
|
|
|
171
187
|
1. Hold a Socratic deep-dive to fill every section of `templates/slice.md`: intent, trigger/actor,
|
|
172
188
|
command + field table (types & rules), event(s) + payload (mark immutable facts), invariants
|
|
173
189
|
(give each a stable ID), Given/When/Then scenarios (happy path + rule boundaries + edge cases),
|
|
174
|
-
alternate/error flows (retries, idempotency, compensations),
|
|
175
|
-
|
|
176
|
-
|
|
190
|
+
alternate/error flows (retries, idempotency, compensations), non-functional requirements
|
|
191
|
+
(security/authz, PII/compliance, performance/SLA), read models affected, open questions. Park
|
|
192
|
+
anything unresolved rather than guessing. If the slice lives in an existing codebase, Grep/Read
|
|
193
|
+
adjacent real sources (OpenAPI specs, DB migrations, existing DTOs/event classes in sibling
|
|
194
|
+
contexts) before finalizing field names/types or invariants — don't guess a shape that's
|
|
195
|
+
already defined elsewhere.
|
|
196
|
+
2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name). Record the originating
|
|
197
|
+
need (ticket/conversation link) in the Intent section when one exists.
|
|
177
198
|
3. Wire it into the `.em`: add `note "slices/<slice-name>.md"` to the slice's primary element
|
|
178
199
|
(the command for State Change, the view for State View, the processor for Automation, the
|
|
179
200
|
translation for Translation).
|
|
180
|
-
4. Update `README.md`'s slice index
|
|
201
|
+
4. Update `README.md`'s slice index — the one canonical slice table (`draft` →
|
|
202
|
+
`ready-to-implement`, later `implemented` once shipped, with `Implemented in:` filled in).
|
|
181
203
|
5. Re-render and `em validate`.
|
|
182
204
|
|
|
205
|
+
## Phase: `conform` — drift check against the codebase
|
|
206
|
+
|
|
207
|
+
Goal: check a **ratified** model (and its slice docs) against the codebase that implements it,
|
|
208
|
+
and report where they've drifted — advisory only, never a gate, never an unprompted edit.
|
|
209
|
+
|
|
210
|
+
**Read `reference/conform.md` before doing any conform work** — it carries the full flow
|
|
211
|
+
(scope, evidence-first verification, `em diff --json`, classification, report) and the
|
|
212
|
+
stance guardrails (evidence-first, uncertainty is never drift, propose-don't-edit). It reuses
|
|
213
|
+
`extract`'s sourcing/mode rules for reading the target codebase rather than duplicating them.
|
|
214
|
+
|
|
215
|
+
In short: for each in-scope slice, gather code evidence *before* comparing to the model or doc;
|
|
216
|
+
write the as-is picture into a scratch model (`<model-name>-asis.em`, reusing the canonical
|
|
217
|
+
model's names wherever the code matches them); run `em diff <model-name>.em
|
|
218
|
+
<model-name>-asis.em --json` and let `em` decide the structural deltas; classify every finding
|
|
219
|
+
(real drift / model gap / internal inconsistency / uncertainty) with cited evidence; write
|
|
220
|
+
`conformance/<date>-report.md` with proposed `issue "conformance: …"` red notes; apply only the
|
|
221
|
+
proposals the user ratifies, then re-render and validate; update the state file's
|
|
222
|
+
`Last conformance:` marker.
|
|
223
|
+
|
|
224
|
+
End of phase: state file's `Last conformance:` marker updated, Decisions log entry if any
|
|
225
|
+
proposals were applied. Conform doesn't chain to another phase — it's a recurring loop, run
|
|
226
|
+
again whenever the codebase has moved.
|
|
227
|
+
|
|
183
228
|
## Phase: `watch` — live team view
|
|
184
229
|
|
|
185
230
|
**Recommended (instant push, no polling):** start the watcher with `--serve` in the background:
|
|
@@ -199,6 +244,11 @@ Run `em validate <model-name>.em` and walk through each diagnostic with the user
|
|
|
199
244
|
rule (see `reference/em-dsl.md`) and proposing the fix (e.g. split an automation's command into
|
|
200
245
|
the next slice, add a missing `from`, give a command its event). Apply fixes on agreement.
|
|
201
246
|
|
|
247
|
+
For anything genuinely unresolved rather than a rule violation, prefer `issue "text"` on the
|
|
248
|
+
relevant element over a `# TBD` comment — it shows up as a red marker on the rendered diagram and
|
|
249
|
+
`em validate` tracks it as an open-issue warning, so it isn't lost once rendered. `em validate
|
|
250
|
+
--list-issues` gives a quick sweep of everything still open.
|
|
251
|
+
|
|
202
252
|
Then do one check `em validate` can't: scan every `translation`/`processor`/`automation` slice and
|
|
203
253
|
confirm none contains an `event` — each reaction must trigger a `command` in the next slice
|
|
204
254
|
(`reaction → command → event`). If you find a reaction wired straight to an event, split it into
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Conform Phase — Drift Detection Against the Codebase
|
|
2
|
+
|
|
3
|
+
Read this **before doing any conform work**. It governs the `conform` phase: checking a
|
|
4
|
+
**ratified** model (and its slice docs) against the codebase that is supposed to implement it,
|
|
5
|
+
and reporting where they've drifted apart. Conform reuses `extract`'s sourcing/mode rules
|
|
6
|
+
(`reference/extract.md`) for reading the target codebase rather than duplicating them — the
|
|
7
|
+
difference is what you do with what you find: extract *builds* a model from the code; conform
|
|
8
|
+
*compares* code evidence against a model that already exists.
|
|
9
|
+
|
|
10
|
+
## Stance — advisory-only, evidence-first
|
|
11
|
+
|
|
12
|
+
- **Advisory-only.** Conform never gates anything and never edits the canonical model or a
|
|
13
|
+
slice doc on its own initiative. It proposes; the user ratifies. There is no such thing as
|
|
14
|
+
an auto-applied conformance fix.
|
|
15
|
+
- **Evidence-first, not model-first.** For every slice you check, gather code evidence —
|
|
16
|
+
handler/event/projection/endpoint sites, invariant-enforcement sites, scenario-test sites —
|
|
17
|
+
**before** you compare anything to the model or doc. Read the doc and model first to know
|
|
18
|
+
*what to look for*, but write down what the code actually shows before you judge whether it
|
|
19
|
+
matches. Anchoring bias is the failure mode this rule exists to block: "the model says X" is
|
|
20
|
+
never allowed to substitute for "the code shows X at `path/to/File.kt:42`".
|
|
21
|
+
- **Uncertainty is never drift.** If you can't find evidence either way — the code path is
|
|
22
|
+
unclear, the test is ambiguous, you ran out of budget — that's an **uncertainty**, reported
|
|
23
|
+
in its own section, never folded into a drift or gap finding. A false positive here is worse
|
|
24
|
+
than a missed one; it's what burns the loop's credibility.
|
|
25
|
+
- **Anchored, not blind.** You read the canonical model's vocabulary before walking the code,
|
|
26
|
+
and you reuse its slice/element names wherever the code genuinely matches them. This trades
|
|
27
|
+
some independence for precision: naming a scratch model's elements from scratch (blind
|
|
28
|
+
extraction) would report wholesale slice add/remove noise from naming alone the moment your
|
|
29
|
+
synonym for an existing element doesn't match the model's — the exact false-drift failure
|
|
30
|
+
mode this phase exists to avoid. The guardrail against anchoring bias is the evidence-first
|
|
31
|
+
rule above, not independence from the model's vocabulary.
|
|
32
|
+
|
|
33
|
+
## Preconditions
|
|
34
|
+
|
|
35
|
+
1. A canonical `.em` model with a state file (`.event-modeling.md`), reachable the same way the
|
|
36
|
+
SKILL.md preconditions locate it.
|
|
37
|
+
2. The model should be ratified for the slices you're about to check — `slice` docs at
|
|
38
|
+
`implemented` (or the status the user says corresponds to shipped code) for whichever slices
|
|
39
|
+
are in scope. Checking a `draft` or `ready-to-implement` slice against code is a wasted walk;
|
|
40
|
+
tell the user and skip it rather than reporting "drift" against a doc nobody built to yet.
|
|
41
|
+
3. The codebase to check: the state file's **Existing system refs** (populated during
|
|
42
|
+
`extract`, if this model came from one) or ask the user for the repo path if not set. Record
|
|
43
|
+
it if missing. In a headless/scheduled run with no user to ask, default to the repository
|
|
44
|
+
the model lives in (if it contains the implementation) and state that assumption in the
|
|
45
|
+
report's run metadata.
|
|
46
|
+
4. Confirm `em --version` works (same check as every other phase) — you'll run `em diff`. The
|
|
47
|
+
phase needs `em diff --json`; if the flag errors as unknown, the installed `em` predates it
|
|
48
|
+
— say so and stop rather than falling back to eyeballing the two models.
|
|
49
|
+
|
|
50
|
+
## The three conformance surfaces
|
|
51
|
+
|
|
52
|
+
The model isn't just the `.em` — slice docs carry the detailed spec (field tables with rules,
|
|
53
|
+
named invariants, Given/When/Then scenarios). Conform checks all three surfaces that can drift:
|
|
54
|
+
|
|
55
|
+
| Surface | What drifts | Who checks | How |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| **Structural** — `.em` ↔ code | slices/elements/fields/flow | `em` (deterministic) | `em diff --json` on canonical vs. an as-is scratch model |
|
|
58
|
+
| **Spec** — slice doc ↔ code | field rules not enforced, named invariants (`INV-*`) without enforcement/test sites, GWT scenarios without test sites | you (judgment) | per-slice evidence walk: each checkable claim in the doc mapped to a code/test site by name (`INV-CHK-4` → its enforcement site + its test) |
|
|
59
|
+
| **Internal** — slice doc ↔ `.em` | the doc's header metadata (Pattern, Status) or its Command/Event/Read Model sections and field tables disagreeing with the model's own elements/fields | you (judgment, v1) | cross-check the doc's structured sections against the slice's model elements |
|
|
60
|
+
|
|
61
|
+
The internal surface is deterministically checkable in principle (structured doc sections +
|
|
62
|
+
`{ fields }` blocks) — promoting it to an `em validate` rule is a natural follow-up; file it as
|
|
63
|
+
its own issue rather than growing this phase's scope. For now it's agent judgment, same as spec.
|
|
64
|
+
|
|
65
|
+
**Internal inconsistency is its own class, not drift.** When a doc and the `.em` disagree with
|
|
66
|
+
each other, neither one is "the code" — you don't know which is stale. Report it as internal
|
|
67
|
+
inconsistency and let the user decide which side is right; don't guess by treating one as
|
|
68
|
+
authoritative.
|
|
69
|
+
|
|
70
|
+
## Flow
|
|
71
|
+
|
|
72
|
+
### 1. Scope
|
|
73
|
+
|
|
74
|
+
Default: **diff-scoped.** Read the state file's `Last conformance:` marker (date + target-repo
|
|
75
|
+
revision). If it's set, run `git diff --name-only <recorded-revision>..HEAD` from the target
|
|
76
|
+
repo's root to list the changed paths (`git log` alone prints commit subjects, not paths), then
|
|
77
|
+
map those paths to slices (via each slice doc's known code locations, `Implemented in:` links,
|
|
78
|
+
or a quick Grep if those are stale) — that's your in-scope set. If the marker reads `never`
|
|
79
|
+
(first run) or the user passes `--full`, scope is every `implemented` slice.
|
|
80
|
+
|
|
81
|
+
Cost containment here is cadence, not a trigger condition — diff-scoped-by-default is what
|
|
82
|
+
keeps a recurring conform run cheap; don't invent a smarter trigger.
|
|
83
|
+
|
|
84
|
+
### 2. Anchored, evidence-first verification (per in-scope slice)
|
|
85
|
+
|
|
86
|
+
For each slice in scope:
|
|
87
|
+
|
|
88
|
+
1. Read the slice's **doc** (`slices/<slice-name>.md`) alongside its elements in the `.em`.
|
|
89
|
+
This tells you the vocabulary to anchor on and the claims to check — not the answer.
|
|
90
|
+
2. Walk the code and **record evidence before comparing**:
|
|
91
|
+
- **Structural:** the handler, event class(es), projection(s), endpoint(s) that implement
|
|
92
|
+
this slice.
|
|
93
|
+
- **Spec:** for every named invariant (`INV-*`) in the doc, its enforcement site and its
|
|
94
|
+
test site (by name/path, both or neither — a half-found invariant is an uncertainty, not
|
|
95
|
+
a pass). Scenarios are budgeted: map Given/When/Then scenarios to individual test sites
|
|
96
|
+
when the in-scope slice count is small (roughly ≤5) or the user asks for that depth;
|
|
97
|
+
otherwise record slice-level scenario-test presence and say so in the report — per-scenario
|
|
98
|
+
mapping across a full model roughly doubles the walk. Where the doc doesn't name
|
|
99
|
+
invariants/scenarios individually, fall back to slice-level test presence (some vs. none)
|
|
100
|
+
rather than inventing IDs the doc never gave you.
|
|
101
|
+
- **Legitimate non-materialized views:** an automation's watched view may have no
|
|
102
|
+
materialized projection in code — a direct event handler with criteria-sourced state is
|
|
103
|
+
the same pattern expressed in the code idiom (extract.md's R5 reaction rules). That is
|
|
104
|
+
NOT structural drift; note the idiom in the evidence log instead of reporting the view
|
|
105
|
+
as missing.
|
|
106
|
+
3. Write what you found into the scratch model (`<model-name>-asis.em`, see below). **Seed the
|
|
107
|
+
scratch model as a copy of the canonical `.em` and replace only the in-scope slices** with
|
|
108
|
+
the as-is picture, leaving out-of-scope slices byte-identical. This is not an optimization:
|
|
109
|
+
`em diff` matches slices by name, so a scratch model containing *only* the in-scope slices
|
|
110
|
+
reports every other canonical slice as `slice-removed` plus one `element-removed` per
|
|
111
|
+
element — a phantom-removal flood on every diff-scoped run, which is the exact false drift
|
|
112
|
+
this phase exists to avoid. It also keeps the scratch model compilable (see step 3). Within
|
|
113
|
+
the slices you do rewrite, **reuse the canonical model's slice and element names wherever
|
|
114
|
+
the code genuinely matches them** — the canonical model is the vocabulary anchor. Give a
|
|
115
|
+
new name only to code behavior the model doesn't cover at all. Record the mapping decision
|
|
116
|
+
for every element you name this way (e.g. `CartItemAdded` code class ↔ `"Cart Item Added"`
|
|
117
|
+
model event) so it's auditable, not a silent judgment call. **Match the canonical model's field granularity:** declare `{ fields }`
|
|
118
|
+
in the scratch model only where the canonical model declares them (if the canonical model
|
|
119
|
+
puts fields on commands but not events, so does the scratch model — writing code-derived
|
|
120
|
+
event fields there would flood the diff with `field-added` noise). Schema claims at finer
|
|
121
|
+
granularity than the `.em` declares — e.g. event fields documented only in a slice doc's
|
|
122
|
+
table — are checked on the **spec surface** against the doc, not smuggled into the
|
|
123
|
+
structural diff.
|
|
124
|
+
4. Cross-check the doc's header metadata and Command/Event/Read Model sections against the
|
|
125
|
+
slice's actual `.em` elements and fields for the internal surface — this doesn't need code
|
|
126
|
+
evidence, just doc-vs-`.em` reading. Narrative sections (Intent, Open Questions, notes)
|
|
127
|
+
count too when the contradiction is unambiguous — a stale sentence that flatly contradicts
|
|
128
|
+
the `.em` is an internal inconsistency; a vague or interpretable one is not a finding.
|
|
129
|
+
|
|
130
|
+
Do this for every in-scope slice before moving on to diffing. The evidence you record here is
|
|
131
|
+
what every finding in the report will cite — file paths, not vibes.
|
|
132
|
+
|
|
133
|
+
### 3. Deterministic diff
|
|
134
|
+
|
|
135
|
+
First `em validate <model-name>-asis.em`. `em diff` refuses when either side has validation
|
|
136
|
+
*errors* (warnings are fine), so a scratch model that doesn't compile stops the run cold. The
|
|
137
|
+
usual cause is a slice that depends on one you rewrote incompletely — a `view X again` whose
|
|
138
|
+
first declaration you dropped, or an automation `from` a read model that no longer exists.
|
|
139
|
+
Seeding from the canonical model (step 2) prevents this; if it still fails, **fix the scratch
|
|
140
|
+
model and report the failure as a tooling problem in the run metadata — never as drift.** A
|
|
141
|
+
model you couldn't compile is an uncertainty about your own scratch file, not a finding
|
|
142
|
+
against the codebase.
|
|
143
|
+
|
|
144
|
+
Then run `em diff <model-name>.em <model-name>-asis.em --json`. `em`, not you, decides what
|
|
145
|
+
structurally differs — don't re-derive by eye what the tool already computed. Parse the JSON
|
|
146
|
+
(`docs/cli.md`'s diff section documents the envelope: `diffSchemaVersion`, `counts`, `changes`,
|
|
147
|
+
`removals`, every optional `ChangeEntry` field explicit-`null` when unused) and use it as the
|
|
148
|
+
structural-surface findings directly.
|
|
149
|
+
|
|
150
|
+
### 4. Classify
|
|
151
|
+
|
|
152
|
+
Every finding — structural deltas from step 3, plus spec- and internal-surface findings from
|
|
153
|
+
step 2 — sorts into exactly one class:
|
|
154
|
+
|
|
155
|
+
- **Real drift** — code evidence contradicts the model or doc.
|
|
156
|
+
- **Model gap** — code evidence shows behavior neither the model nor the doc covers.
|
|
157
|
+
- **Internal inconsistency** — the doc and the `.em` disagree with each other (surface 3 only).
|
|
158
|
+
- **Extraction uncertainty** — no evidence either way. Never reported as drift or a gap.
|
|
159
|
+
|
|
160
|
+
Every classification must cite the evidence recorded in step 2 (or the `em diff` entry, for
|
|
161
|
+
structural findings) — a finding with no citation isn't ready to report.
|
|
162
|
+
|
|
163
|
+
### 5. Report + proposals
|
|
164
|
+
|
|
165
|
+
Write `conformance/<YYYY-MM-DD>-report.md` in the model directory, from
|
|
166
|
+
`templates/conformance-report.md`. For a real-drift or model-gap finding, propose a
|
|
167
|
+
ready-to-apply red note — `issue "conformance: <text>"` on the right element, written out in
|
|
168
|
+
the report exactly as it should be pasted into the `.em` — when a model-side marker is the
|
|
169
|
+
right fix; when the fix is purely doc wording (or the finding is an internal inconsistency
|
|
170
|
+
with no clear side to flag), omit the red note and say why instead. **You never edit the
|
|
171
|
+
canonical model or a slice doc unprompted** — walk the report with the user, apply only the
|
|
172
|
+
proposals they approve, then re-render and `em validate`.
|
|
173
|
+
|
|
174
|
+
End of run: update the state file's `Last conformance:` marker, exact format:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
- **Last conformance:** YYYY-MM-DD @ <target-repo revision> — report: conformance/YYYY-MM-DD-report.md
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The revision is the one you just diffed against, so the next run's scope starts from here —
|
|
181
|
+
step 1 parses this line, so keep the format exact. If any proposals were ratified and applied,
|
|
182
|
+
log a Decisions entry noting what changed and why.
|
|
183
|
+
|
|
184
|
+
## Conventions
|
|
185
|
+
|
|
186
|
+
- **Scratch model:** `<model-name>-asis.em`, written **next to** the canonical model (same
|
|
187
|
+
directory as `.event-modeling.md`). Always seeded as a copy of the canonical model with only
|
|
188
|
+
the in-scope slices rewritten (step 2), never built up from nothing. It's regenerated every
|
|
189
|
+
conform run — add the pattern `*-asis.em` to the repository's `.gitignore` (create the entry
|
|
190
|
+
if there isn't one) so it never gets committed.
|
|
191
|
+
- **Report location:** `conformance/<YYYY-MM-DD>-report.md` in the model directory. One file
|
|
192
|
+
per run; don't overwrite a prior date's report.
|
|
193
|
+
- **Red note wording:** `issue "conformance: <what's wrong, plainly>"` — plain enough that
|
|
194
|
+
whoever ratifies it later understands the claim without re-reading the report.
|
|
195
|
+
- **Naming/matching rules:** same past-tense-event / imperative-command / thing-shown-view
|
|
196
|
+
naming discipline as the rest of the skill (`methodology.md`'s Socratic stance) — you're
|
|
197
|
+
matching code to existing names here, not inventing new ones except for genuinely uncovered
|
|
198
|
+
behavior.
|
|
199
|
+
|
|
200
|
+
## Completion & handoff
|
|
201
|
+
|
|
202
|
+
Conform is complete when: every in-scope slice has been walked (step 2), the scratch model has
|
|
203
|
+
been diffed (step 3), every finding is classified with cited evidence (step 4), the report is
|
|
204
|
+
written (step 5), the state file's `Last conformance:` marker is updated, and the user has seen
|
|
205
|
+
the report and said which proposals (if any) to apply. Applied proposals get re-rendered and
|
|
206
|
+
re-validated before you call the run done.
|
|
207
|
+
|
|
208
|
+
Conform doesn't chain to another phase automatically — it's a recurring loop, not a step in
|
|
209
|
+
building the model. Suggest running it again next time the target codebase has moved, at
|
|
210
|
+
whatever cadence the user wants (see `docs/ci.md` for a scheduled-run recipe).
|
|
211
|
+
|
|
212
|
+
## Anti-patterns
|
|
213
|
+
|
|
214
|
+
- **Blind extraction** — deriving the as-is scratch model with no sight of the canonical
|
|
215
|
+
model's vocabulary. Loses the anchor and produces naming-driven false drift.
|
|
216
|
+
- **A scratch model holding only the in-scope slices** — `em diff` reads every omitted
|
|
217
|
+
canonical slice as removed, so a diff-scoped run drowns in phantom removals. Seed from the
|
|
218
|
+
canonical model and rewrite only the slices you walked.
|
|
219
|
+
- **Reporting uncertainty as drift** — "I couldn't find the enforcement site" is an
|
|
220
|
+
uncertainty, never a finding against the model.
|
|
221
|
+
- **Editing the model or a slice doc without ratification** — conform proposes; the user
|
|
222
|
+
applies. No exceptions, even for an "obviously correct" fix.
|
|
223
|
+
- **Treating scenario/test absence as a build failure** — a missing test site is a spec-surface
|
|
224
|
+
finding to report and classify, not a reason to stop the run or block anything. Conform is
|
|
225
|
+
advisory; there's no gate here.
|
|
226
|
+
- **Re-deriving what `em diff` already decided** — once you have the JSON, use it; don't
|
|
227
|
+
eyeball the two `.em` files yourself looking for structural differences the tool already
|
|
228
|
+
computed for you.
|
|
@@ -18,6 +18,8 @@ em render <file> --keep-empty-lanes # keep the API lane even when empty
|
|
|
18
18
|
em watch <file> -o out.svg # re-render on every save (file-based)
|
|
19
19
|
em watch <file> -o out.svg --serve # + localhost live viewer, instant SSE push-reload (--port N)
|
|
20
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
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
Install if missing: `npm i -g @milehimikey/em`. PNG works with no system deps; PDF needs
|
|
@@ -48,11 +50,11 @@ arrow From Element -> To Element # explicit cross-slice edge (overrides infer
|
|
|
48
50
|
### Element kinds (8 keywords, nothing else)
|
|
49
51
|
| Keyword | Band | Meaning | Tag | Extra clauses |
|
|
50
52
|
|---|---|---|---|---|
|
|
51
|
-
| `ui` | persona | screen / interface | `@Persona` | `note`, `{ fields }` |
|
|
52
|
-
| `command` | API | state-changing request | — | `note`, `{ fields }` |
|
|
53
|
-
| `view` | API | read model / projection | — | `from "Event"…`, `note`, `{ fields }` |
|
|
54
|
-
| `event` | context | recorded fact (past tense) | `@Context` | `note`, `{ fields }` |
|
|
55
|
-
| `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 }` |
|
|
56
58
|
|
|
57
59
|
### Clauses
|
|
58
60
|
- **Tags:** `@Persona` only on `ui`; `@Context` only on `event`. Undeclared tags auto-create a row.
|
|
@@ -69,6 +71,11 @@ arrow From Element -> To Element # explicit cross-slice edge (overrides infer
|
|
|
69
71
|
`from`/`arrow` — plain repeats are only warning-free while unreferenced.
|
|
70
72
|
- **`note "path.md"`** on ANY element links a markdown doc. Relative to the `.em` file. Renders as
|
|
71
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.
|
|
72
79
|
- **Fields:** `command Place Order { orderId: UUID, items: LineItem[], customerId }` — inline or
|
|
73
80
|
one-per-line. Types are free text (no semantic checking). Keep these light; full field specs
|
|
74
81
|
with rules live in the slice doc.
|
|
@@ -197,6 +204,16 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
|
|
|
197
204
|
2. **Command without event** — every command should record at least one event.
|
|
198
205
|
3. **Read model without source** — add `from "Event"` or place the view in a slice with an event.
|
|
199
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.
|
|
200
217
|
|
|
201
218
|
**Design rules that keep models valid:**
|
|
202
219
|
- One element per band per slice (multiple personas/contexts are fine — they're different rows).
|
|
@@ -94,8 +94,9 @@ one for a sprawling one. Record any deviation in the Decisions log.
|
|
|
94
94
|
- Inline `.em` comment on its own line directly above (or trailing) the element it concerns:
|
|
95
95
|
`# TBD: <specific question about current behavior>`. Comments are native DSL syntax — they
|
|
96
96
|
render and validate cleanly.
|
|
97
|
-
- Mirror every `# TBD` 1:1 as a checkbox in the state file's **Open questions / parking lot
|
|
98
|
-
and remove both together when
|
|
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.
|
|
99
100
|
- A `# TBD` is only ever resolved by a confirmed current-state fact — never replaced by a
|
|
100
101
|
guessed element.
|
|
101
102
|
|
|
@@ -105,8 +106,8 @@ Extraction is complete when: the model renders; `em validate` is clean (or remai
|
|
|
105
106
|
are consciously accepted in the Decisions log); every unknown is a parked `# TBD`; and the
|
|
106
107
|
user has confirmed the model reflects today's behavior. Then:
|
|
107
108
|
|
|
108
|
-
1. State file: set phase progress (extraction rounds checked), log the completion decision
|
|
109
|
-
|
|
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`).
|
|
110
111
|
2. The extracted model **replaces `discover`** — suggest `/event-modeling model` next
|
|
111
112
|
(steps 5-7: swimlanes, patterns, completeness). Desired-state changes belong *after* the
|
|
112
113
|
as-is model is agreed — as normal `model`/`slice` evolution, clearly separated from
|
|
@@ -163,6 +163,9 @@ dedicated `slice` phase writes the full rich spec (see `templates/slice.md`).
|
|
|
163
163
|
- Prompts: *"Given what starting state, when this command/trigger fires, then what event(s)
|
|
164
164
|
result? What must always be true? What are the failure paths? Which fields are essential and
|
|
165
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.
|
|
166
169
|
- Output: rich slice docs linked into the `.em` via `note "slices/<name>.md"`.
|
|
167
170
|
|
|
168
171
|
### Step 7 — Evaluate Completeness *(model)*
|
|
@@ -192,9 +195,11 @@ The stance inversion, stated authoritatively: **extraction captures how the syst
|
|
|
192
195
|
today — faithfully, warts included — and never invents future or desired state.** Step 1's
|
|
193
196
|
anti-current-system caution is suspended for the duration; the "is it an event?" filter is
|
|
194
197
|
not — derived values and telemetry are still rejected even when the current system emits them.
|
|
195
|
-
Unknown or ambiguous current behavior is parked
|
|
196
|
-
|
|
197
|
-
`
|
|
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.
|
|
198
203
|
|
|
199
204
|
Procedure, source modes (event-driven vs. procedural synthesis), and the round-by-round loop:
|
|
200
205
|
`reference/extract.md`.
|
|
@@ -208,3 +213,6 @@ Procedure, source modes (event-driven vs. procedural synthesis), and the round-b
|
|
|
208
213
|
- Mirror the model back after each increment and **re-render** so the team sees it evolve.
|
|
209
214
|
- Park unresolved questions in the state file instead of guessing.
|
|
210
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).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Conformance report skeleton. One per conform run, stored at
|
|
3
|
+
<model-name>/conformance/<YYYY-MM-DD>-report.md (see reference/conform.md).
|
|
4
|
+
Fill every section; leave "none" rather than deleting a section with nothing to report.
|
|
5
|
+
Replace bracketed placeholders; delete guidance comments before finishing.
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Conformance Report — {{Model Name}} — {{YYYY-MM-DD}}
|
|
9
|
+
|
|
10
|
+
- **Model:** `{{model-name}}.em`
|
|
11
|
+
- **Target repo:** {{repo path or URL}} @ `{{revision}}`
|
|
12
|
+
- **Previous conformance revision:** {{prior target-repo revision, or "never (first run)"}}
|
|
13
|
+
- **Scope:** {{full | diff-scoped since <prior revision>}}
|
|
14
|
+
- **Slices checked:** {{comma-separated slice names, or "all implemented slices"}}
|
|
15
|
+
- **Slices skipped (not yet implemented):** {{list, or "none"}}
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
|
|
19
|
+
| Surface | Real drift | Model gap | Internal inconsistency | Uncertainty |
|
|
20
|
+
|---|---|---|---|---|
|
|
21
|
+
| Structural | {{n}} | {{n}} | — | {{n}} |
|
|
22
|
+
| Spec | {{n}} | {{n}} | — | {{n}} |
|
|
23
|
+
| Internal | — | — | {{n}} | {{n}} |
|
|
24
|
+
|
|
25
|
+
{{One or two sentences: overall read — clean, mostly clean with N items, or notably drifted.}}
|
|
26
|
+
|
|
27
|
+
## Findings
|
|
28
|
+
|
|
29
|
+
<!-- One subsection per finding. Group however reads clearest (by slice, or by surface) but keep
|
|
30
|
+
every field below for every finding. Order doesn't matter; classification does. -->
|
|
31
|
+
|
|
32
|
+
### {{n}}. {{Short finding title}}
|
|
33
|
+
|
|
34
|
+
- **Surface:** {{structural | spec | internal}}
|
|
35
|
+
- **Classification:** {{real drift | model gap | internal inconsistency}}
|
|
36
|
+
- **Slice:** {{slice name}}
|
|
37
|
+
- **Evidence:** {{file path(s) and line/symbol, or the `em diff --json` entry (type + name)}}
|
|
38
|
+
- **Claim:** {{what the model/doc says vs. what the code/doc shows — the actual disagreement}}
|
|
39
|
+
- **Proposed red note:** `issue "conformance: {{text}}"` on {{element}} (slice "{{slice}}")
|
|
40
|
+
<!-- Omit this line entirely for internal-inconsistency findings with no clear side to flag,
|
|
41
|
+
or when the finding doesn't warrant a red note (say why instead). -->
|
|
42
|
+
|
|
43
|
+
## Scenario / invariant coverage
|
|
44
|
+
|
|
45
|
+
<!-- Per in-scope slice: every named invariant and every GWT scenario the doc claims, mapped to
|
|
46
|
+
its enforcement/test site. Leave a cell explicitly blank/"none found" rather than omitting
|
|
47
|
+
the row — gaps are the point of this table. -->
|
|
48
|
+
|
|
49
|
+
### {{Slice Name}}
|
|
50
|
+
|
|
51
|
+
| Claim | Type | Enforcement site | Test site | Status |
|
|
52
|
+
|---|---|---|---|---|
|
|
53
|
+
| {{INV-XXX-1 or scenario title}} | {{invariant \| scenario}} | {{file path / symbol, or "none found"}} | {{file path / symbol, or "none found"}} | {{covered \| gap \| uncertain}} |
|
|
54
|
+
|
|
55
|
+
## Uncertainties
|
|
56
|
+
|
|
57
|
+
<!-- Listed separately from Findings — these are NOT drift. No evidence either way; say what was
|
|
58
|
+
missing (time, clarity, an ambiguous test) so a follow-up run can pick it up. -->
|
|
59
|
+
|
|
60
|
+
- **{{structural | spec | internal}}** — {{what's uncertain}} — {{slice}} — {{why: e.g. "couldn't locate the test for INV-CHK-3, only its enforcement site"}}
|
|
61
|
+
|
|
62
|
+
<!-- Tag each with its surface so these reconcile against the Summary table's Uncertainty column. -->
|
|
63
|
+
|
|
64
|
+
## Applied this run
|
|
65
|
+
|
|
66
|
+
<!-- Filled in AFTER the user reviews Findings and says what to apply. Leave "none yet — awaiting
|
|
67
|
+
ratification" until that conversation happens. -->
|
|
68
|
+
|
|
69
|
+
{{List of ratified proposals actually applied to the `.em`/slice docs, or "none yet — awaiting ratification".}}
|
|
@@ -22,10 +22,12 @@ Static render: `em render {{model-name}}.em -o {{model-name}}.svg`
|
|
|
22
22
|
- **Translation** — External input (or Read Model) → Translation → (next slice) Command → Event
|
|
23
23
|
|
|
24
24
|
## Slices
|
|
25
|
-
<!--
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
|
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) |
|
|
29
31
|
|
|
30
32
|
## Status
|
|
31
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:** {{...}}
|