@milehimikey/em 1.6.1 → 1.7.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.
Files changed (105) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +94 -49
  2. package/.claude/skills/event-modeling/reference/conform.md +30 -8
  3. package/.claude/skills/event-modeling/reference/em-dsl.md +194 -73
  4. package/.claude/skills/event-modeling/reference/extract.md +14 -11
  5. package/.claude/skills/event-modeling/reference/implement.md +172 -0
  6. package/.claude/skills/event-modeling/reference/methodology.md +40 -33
  7. package/.claude/skills/event-modeling/templates/conformance-report.md +15 -9
  8. package/.claude/skills/event-modeling/templates/model-readme.md +7 -7
  9. package/.claude/skills/event-modeling/templates/slice.md +73 -7
  10. package/.claude/skills/event-modeling/templates/state.md +7 -6
  11. package/README.md +1 -0
  12. package/dist/catalog/build.d.ts +5 -0
  13. package/dist/catalog/build.js +1 -3
  14. package/dist/catalog/build.js.map +1 -1
  15. package/dist/catalog/classify.d.ts +7 -8
  16. package/dist/catalog/classify.js +7 -8
  17. package/dist/catalog/classify.js.map +1 -1
  18. package/dist/catalog/docJoin.d.ts +39 -0
  19. package/dist/catalog/docJoin.js +86 -0
  20. package/dist/catalog/docJoin.js.map +1 -0
  21. package/dist/catalog/driftSignal.d.ts +16 -0
  22. package/dist/catalog/driftSignal.js +24 -0
  23. package/dist/catalog/driftSignal.js.map +1 -0
  24. package/dist/catalog/frontmatterCoherenceValidate.d.ts +8 -0
  25. package/dist/catalog/frontmatterCoherenceValidate.js +52 -0
  26. package/dist/catalog/frontmatterCoherenceValidate.js.map +1 -0
  27. package/dist/catalog/lineageValidate.d.ts +9 -0
  28. package/dist/catalog/lineageValidate.js +161 -0
  29. package/dist/catalog/lineageValidate.js.map +1 -0
  30. package/dist/catalog/readSliceDoc.d.ts +2 -0
  31. package/dist/catalog/readSliceDoc.js +21 -0
  32. package/dist/catalog/readSliceDoc.js.map +1 -0
  33. package/dist/catalog/sliceDoc.d.ts +94 -3
  34. package/dist/catalog/sliceDoc.js +157 -7
  35. package/dist/catalog/sliceDoc.js.map +1 -1
  36. package/dist/catalog/sliceReadyValidate.d.ts +11 -0
  37. package/dist/catalog/sliceReadyValidate.js +85 -0
  38. package/dist/catalog/sliceReadyValidate.js.map +1 -0
  39. package/dist/cli/diff-inputs.d.ts +20 -0
  40. package/dist/cli/diff-inputs.js +75 -18
  41. package/dist/cli/diff-inputs.js.map +1 -1
  42. package/dist/cli/ledgerCheck.d.ts +38 -0
  43. package/dist/cli/ledgerCheck.js +116 -0
  44. package/dist/cli/ledgerCheck.js.map +1 -0
  45. package/dist/cli/skillCheck.d.ts +18 -0
  46. package/dist/cli/skillCheck.js +63 -0
  47. package/dist/cli/skillCheck.js.map +1 -0
  48. package/dist/cli/skillSync.d.ts +22 -0
  49. package/dist/cli/skillSync.js +69 -0
  50. package/dist/cli/skillSync.js.map +1 -0
  51. package/dist/cli/skillVersionCheck.d.ts +32 -0
  52. package/dist/cli/skillVersionCheck.js +145 -0
  53. package/dist/cli/skillVersionCheck.js.map +1 -0
  54. package/dist/cli.d.ts +3 -1
  55. package/dist/cli.js +203 -32
  56. package/dist/cli.js.map +1 -1
  57. package/dist/emit/diffJson.d.ts +1 -1
  58. package/dist/emit/diffJson.js +18 -14
  59. package/dist/emit/diffJson.js.map +1 -1
  60. package/dist/emit/dot.js +46 -5
  61. package/dist/emit/dot.js.map +1 -1
  62. package/dist/emit/json.d.ts +10 -25
  63. package/dist/emit/json.js +70 -103
  64. package/dist/emit/json.js.map +1 -1
  65. package/dist/emit/ledgerJson.d.ts +5 -0
  66. package/dist/emit/ledgerJson.js +25 -0
  67. package/dist/emit/ledgerJson.js.map +1 -0
  68. package/dist/emit/skillCheckJson.d.ts +5 -0
  69. package/dist/emit/skillCheckJson.js +32 -0
  70. package/dist/emit/skillCheckJson.js.map +1 -0
  71. package/dist/model/diff.d.ts +44 -1
  72. package/dist/model/diff.js +51 -9
  73. package/dist/model/diff.js.map +1 -1
  74. package/dist/model/edges.js +17 -17
  75. package/dist/model/edges.js.map +1 -1
  76. package/dist/model/model.d.ts +1 -1
  77. package/dist/model/refs.d.ts +23 -0
  78. package/dist/model/refs.js +75 -0
  79. package/dist/model/refs.js.map +1 -0
  80. package/dist/model/rules.d.ts +253 -0
  81. package/dist/model/rules.js +254 -0
  82. package/dist/model/rules.js.map +1 -0
  83. package/dist/model/validate.d.ts +23 -1
  84. package/dist/model/validate.js +207 -69
  85. package/dist/model/validate.js.map +1 -1
  86. package/dist/parser/ast.d.ts +3 -2
  87. package/dist/parser/lexer.d.ts +17 -2
  88. package/dist/parser/lexer.js +119 -13
  89. package/dist/parser/lexer.js.map +1 -1
  90. package/dist/parser/parser.js +75 -34
  91. package/dist/parser/parser.js.map +1 -1
  92. package/dist/pipeline.d.ts +9 -0
  93. package/dist/pipeline.js +4 -2
  94. package/dist/pipeline.js.map +1 -1
  95. package/dist/render/sliceDiagram.js +33 -35
  96. package/dist/render/sliceDiagram.js.map +1 -1
  97. package/dist/render/sliceStatus.js +1 -1
  98. package/dist/render/sliceStatus.js.map +1 -1
  99. package/dist/util/isMainModule.d.ts +14 -0
  100. package/dist/util/isMainModule.js +33 -0
  101. package/dist/util/isMainModule.js.map +1 -0
  102. package/dist/util/walkDir.d.ts +4 -0
  103. package/dist/util/walkDir.js +28 -0
  104. package/dist/util/walkDir.js.map +1 -0
  105. package/package.json +5 -2
@@ -1,28 +1,26 @@
1
1
  ---
2
2
  name: event-modeling
3
+ em-version: 1.7.1
3
4
  description: >-
4
- Guide a user and their team through building rigorous Event Models with the `em` CLI, using
5
- the 7 steps of Event Modeling and the 4 patterns (State Change, State View, Automation,
6
- Translation), and produce implementation-ready slice design documents. Use when the user wants
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, 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.
5
+ Use when the user wants to event-model a business process or system: building, editing,
6
+ updating, or maintaining a model — extending or detailing an existing one into slices,
7
+ extracting a current-state model from an existing codebase, implementing a ratified slice, or
8
+ checking a model for drift against the code. Drives the `em` CLI through resumable phases —
9
+ discover, extract, model, slice, implement, conform, watch, review, validate — or resumes
10
+ automatically with no argument.
15
11
  ---
16
12
 
17
13
  # Event Modeling with `em`
18
14
 
19
15
  You are facilitating an Event Modeling session. Your job is to **extract an accurate model from
20
16
  the user through Socratic questioning** — never to invent the domain. You drive the `em` CLI to
21
- render the model live, and you produce implementation-ready slice design docs.
17
+ render the model live, and you produce implementation-ready slice docs.
22
18
 
23
19
  Read `reference/methodology.md` (the 7 steps + 4 patterns) and `reference/em-dsl.md` (DSL syntax and
24
20
  validation rules) before doing real work — they are the source of truth. Templates live in
25
- `templates/`.
21
+ `templates/`. References to `docs/*.md` throughout this skill mean the
22
+ [em repository's docs](https://github.com/milehimikey/em/tree/main/docs) — they are not
23
+ vendored alongside this skill.
26
24
 
27
25
  ## Operating principles (every phase)
28
26
 
@@ -55,23 +53,24 @@ validation rules) before doing real work — they are the source of truth. Templ
55
53
  keep a sub-flow that detours into another context together, not parked at the end of the model).
56
54
  Declare every instance after the first with
57
55
  **`view X again from "..."`** — `again` instances are exempt from the duplicate-name warning even
58
- when referenced, and each reference resolves to the right instance. Slice order matters: a
59
- reaction must be directly followed by its command slice, and a read slice must not be immediately
60
- followed by a command slice (see `em-dsl.md`).
56
+ when referenced, and each reference resolves to the right instance. A reaction's command is
57
+ wired by same-slice presence, exactly like `ui → command` — no positional/adjacency rule to get
58
+ right (see `em-dsl.md`).
61
59
  See `reference/methodology.md` (State View) and `reference/em-dsl.md`.
62
60
  - **Never connect two instances of one read model.** The repeat is a timeline device, not a flow:
63
61
  continuity is implied by the shared name, and the events arriving at each instance are what show
64
62
  it changing. An arrow between instances says the read model feeds itself, and is an error.
65
63
  - **Every slice is a COMPLETE pattern, never a half-slice.** A State Change is
66
64
  `ui → command → event`; a State View is `event → read model → ui` (or `→ reaction`). So: a
67
- command needs something that **triggers** it (the `ui` it's issued from, or the reaction in the
68
- slice before it), its event needs a **reader**, and that read model needs a **consumer** of its
69
- own. A command nothing points at is a write nobody can start; an event nothing projects is a
70
- write nobody can see; a read model nothing displays is information dropped on the floor. Write
71
- the whole chain together rather than sweeping up dangling ends later. Reactions don't count as
72
- readers of *events* — they read views. **Every instance** of a repeated read model needs its own
73
- consumer: if you repeat a view next to an event just to keep the arrow short, bring its screen
74
- along, or don't add the instance.
65
+ command needs something that **triggers** it (the `ui` it's issued from, or the reaction that
66
+ triggers it, also in this slice), its event needs a **reader**, and that read model needs a
67
+ **consumer** of its own. A command nothing points at is a write nobody can start; a reaction
68
+ that triggers no command is a decision the system never acts on; an event nothing projects is
69
+ a write nobody can see; a read model nothing displays is information dropped on the floor.
70
+ Write the whole chain together rather than sweeping up dangling ends later. Reactions don't
71
+ count as readers of *events* — they read views. **Every instance** of a repeated read model
72
+ needs its own consumer: if you repeat a view next to an event just to keep the arrow short,
73
+ bring its screen along, or don't add the instance.
75
74
  - **Only six connections are legal:** `ui → command`, `command → event`, `event → read model`,
76
75
  `read model → ui`, `read model → reaction`, `reaction → command`. Reaching for anything else —
77
76
  above all `command → read model` (the CQRS violation) or `read model → command` — means the model
@@ -79,8 +78,8 @@ validation rules) before doing real work — they are the source of truth. Templ
79
78
  - **Validate continuously.** Run `em validate` and fix errors/warnings as you go (see DSL ref).
80
79
  - **Save state at the end of every session** so work resumes cleanly. Also append one line to
81
80
  the Usage log: the phase(s) touched and the `em validate` diagnostic *categories* that fired
82
- (rule name only, e.g. "read model nothing consumes" — never the full message or domain
83
- content). This is the team's only usage signal today (`docs/usage-data.md`) — keep it cheap
81
+ (the exact fixed strings from `docs/usage-data.md`, e.g. "read model has no consumer" —
82
+ never the full message or domain content). This is the team's only usage signal today (`docs/usage-data.md`) — keep it cheap
84
83
  and habitual, not a task to skip.
85
84
 
86
85
  ## Preconditions (run first)
@@ -146,8 +145,9 @@ phase applies to field tables, below.
146
145
  3. **Inputs (step 3).** For each event, find the **command** that causes it (imperative name).
147
146
  Form `command → event` slices (State Change pattern). Every command needs a **trigger** in the
148
147
  same breath: the screen the user issues it from (a `ui` in the slice), or — for the Automation
149
- and Translation patterns — the reaction in the slice before it. A command nothing points at is
150
- a write nobody can start. *"Who does this, and where are they when they do it?"*
148
+ and Translation patterns — the reaction that triggers it, also in this slice. A command
149
+ nothing points at is a write nobody can start. *"Who does this, and where are they when they
150
+ do it?"*
151
151
  4. **Outputs (step 4).** Identify the **read models / views** consumers need and wire them with
152
152
  `from "Event"` (State View pattern), each with the screen (or reaction) that consumes it — a
153
153
  read model nothing displays is information dropped on the floor. In a **headless/API** model
@@ -190,23 +190,23 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
190
190
 
191
191
  1. **Swimlanes & patterns (step 5).** Group events into **contexts** (bounded contexts /
192
192
  aggregates) — ask which events share a consistency boundary and who owns them. Classify each
193
- slice as one of the 4 patterns. **Split every automation _and every translation_ across two
194
- slices** (the reaction — processor/translation — plus its read model in one slice; the
195
- triggered command + its event in the *next* slice). A translation or automation is a reaction:
196
- it **triggers a command and never records an event directly**. Add `translation` slices for
197
- external inputs (externally triggered: external → translation → command → event) and for the
198
- system reacting to its own state (internally triggered: read model → translation → command →
199
- event).
193
+ slice as one of the 4 patterns. **Share the slice between every automation or translation and
194
+ the command it triggers** (the reaction — processor/translation — together with the command
195
+ and event it produces; the read model it watches, if any, stays in the slice before). A
196
+ translation or automation is a reaction: it **triggers a command and never records an event
197
+ directly**. Add `translation` slices for external inputs (externally triggered: external →
198
+ translation → command → event, no slice before) and for the system reacting to its own state
199
+ (internally triggered: read model in the slice before → translation → command → event).
200
200
  2. **Elaborate scenarios — first pass (step 6).** For each slice, capture the happy-path
201
201
  Given/When/Then and the obvious invariants as short notes. (The full spec is the `slice` phase.)
202
202
  3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
203
203
  half-slice — **every command has something that triggers it**, every command emits an event,
204
204
  **every event is read by a read model**, **every read model has a consumer**, every view has a
205
205
  source, every UI is reachable, every connection is
206
- one of the six legal pairs, and automations **and translations** split correctly (each reaction
207
- triggers a command — none wired straight to an event). Run `em validate` and resolve all errors
208
- and warnings — but check the reaction→command→event split by hand, since `em validate` does
209
- **not** flag a translation/automation that emits an event directly (see `reference/em-dsl.md`).
206
+ one of the six legal pairs, and every automation **and** translation triggers a command (none
207
+ wired straight to an event, none with no command at all). Run `em validate` and resolve all
208
+ errors and warnings — it catches a reaction wired straight to an event too now (see
209
+ `reference/em-dsl.md`), so a clean run covers this case.
210
210
  For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
211
211
  this fact and what they do with it. The honest answers are "here's the read model we missed" or
212
212
  "nobody — so why are we recording it", and both improve the model.
@@ -226,6 +226,16 @@ phases is the starting point, not the finished event set. **Every new event need
226
226
  real (someone has to see that the request was declined).
227
227
 
228
228
  For each slice:
229
+ 0. **Check for re-ratification first.** If `slices/<slice-name>.md` already exists with
230
+ `status: implemented`, this is a re-ratification, not fresh authoring: hold a Socratic
231
+ deep-dive on what changed (same rigor as step 1, scoped to the delta), update whichever
232
+ sections actually changed, then express the change as the `## Delta` section — fixed heading,
233
+ `### Added`/`Modified`/`Removed`/`Renamed` Requirement blocks each carrying its own scenarios
234
+ (see `templates/slice.md` and `docs/slice-doc-schema.md#delta-section-grammar-and-lifecycle`)
235
+ — **overwriting** whatever the section held before, never appending to it. Then continue at
236
+ step 2 to bump `version` and flip `status` back to `ready-to-implement`, leaving
237
+ `implementedIn` naming the prior version's PR (intended drift signal, not a bug — see the
238
+ schema doc). Otherwise, this is first-time authoring: continue at step 1.
229
239
  1. Hold a Socratic deep-dive to fill every section of `templates/slice.md`: intent, trigger/actor,
230
240
  command + field table (types & rules), event(s) + payload (mark immutable facts), invariants
231
241
  (give each a stable ID), Given/When/Then scenarios (happy path + rule boundaries + edge cases),
@@ -235,19 +245,51 @@ For each slice:
235
245
  adjacent real sources (OpenAPI specs, DB migrations, existing DTOs/event classes in sibling
236
246
  contexts) before finalizing field names/types or invariants — don't guess a shape that's
237
247
  already defined elsewhere.
238
- 2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name). Record the originating
248
+ 2. Write the doc to `slices/<slice-name>.md` (kebab-case the slice name), with the YAML
249
+ frontmatter block at the very top — all five of `schemaVersion`/`pattern`/`swimlane`/
250
+ `status`/`version` (kebab-case `pattern` value, `schemaVersion: 1`, `version` starting at
251
+ `1`; omitting any of the five makes the doc `frontmatter-invalid` to `em export` and the
252
+ readiness gate), plus `implementedIn` once shipped — the canonical, machine-read
253
+ metadata dialect (`- **Status:** ...` bullet lines are legacy/accepted input only; never
254
+ write new docs that way). When this doc exists because of a split, merge, or rename, add the
255
+ matching lineage key(s) too (`split-from`/`merged-from`/`superseded-by`, `<slice-key>@v<N>`
256
+ grammar — see `docs/slice-doc-schema.md` for the full schema). On a re-ratification (step 0),
257
+ bump `version` and flip `status` here instead of setting them fresh. Record the originating
239
258
  need (ticket/conversation link) in the Intent section when one exists.
240
259
  3. Render the slice's own diagram: `em render <model>.em --slice "<slice name>" -o
241
260
  slices/<slice-name>.svg` (kebab-case, matching the doc's filename) — redraws just this slice
242
261
  in its own canonical pattern shape, and add `![Diagram](./<slice-name>.svg)` near the top of
243
- the doc, right after the metadata bullet list.
262
+ the doc, right after the `# Slice:` heading (below the frontmatter block).
244
263
  4. Wire it into the `.em`: add `note "slices/<slice-name>.md"` to the slice's primary element
245
264
  (the command for State Change, the view for State View, the processor for Automation, the
246
265
  translation for Translation).
247
- 5. Update `README.md`'s slice index — the one canonical slice table (`draft` →
248
- `ready-to-implement`, later `implemented` once shipped, with `Implemented in:` filled in).
266
+ 5. Update `README.md`'s slice index — the one canonical slice table (`draft` → `reviewed` →
267
+ `ready-to-implement`, later `implemented` once shipped, with its Implemented-in link
268
+ filled in).
249
269
  6. Re-render and `em validate`.
250
270
 
271
+ When a slice is **ratified** — a human flips its `status` to `ready-to-implement` with every
272
+ open question resolved — it's ready to hand to an implementer. Confirm with
273
+ `em validate <model>.em --slice-ready <key>`, and point whoever (or whatever) implements it
274
+ at `reference/implement.md`.
275
+
276
+ ## Phase: `implement` — build one ratified slice
277
+
278
+ Goal: turn a single `ready-to-implement` slice into merged, verified code — the one phase
279
+ where the agent builds rather than facilitates.
280
+
281
+ **Read `reference/implement.md` before doing any implement work and follow it as the
282
+ contract.** In short: gate on `em validate <model>.em --slice-ready <key>` (stop and hand
283
+ back if it fails — never edit the doc to make the gate pass); treat the slice doc as the
284
+ spec (read-only, except the merge-time `status`/`implementedIn` flip); surface every gap to
285
+ the user instead of deciding it silently; cover every INV-n and every scenario with tests;
286
+ in spec-kit projects, allocate via em-sdd-bridge (redirect mode) and never run
287
+ `/speckit.specify`.
288
+
289
+ End of phase: PR merged, slice doc flipped to `status: implemented` with `implementedIn`
290
+ filled, `README.md`'s slice index updated. Implement doesn't chain to another phase — run it
291
+ once per ratified slice; `conform` later checks the result against the model.
292
+
251
293
  ## Phase: `conform` — drift check against the codebase
252
294
 
253
295
  Goal: check a **ratified** model (and its slice docs) against the codebase that implements it,
@@ -322,8 +364,8 @@ rule (see `reference/em-dsl.md`) and proposing the fix. Apply fixes on agreement
322
364
 
323
365
  | Diagnostic | Fix |
324
366
  |---|---|
325
- | automation/translation shares a slice with a command | Split the command into the next slice |
326
- | **command has nothing that triggers it** | Add the `ui` it's issued from — or, if the system issues it, the reaction in the slice before it. Ask *"who does this, and where are they when they do it?"* |
367
+ | reaction triggers no command | Add the command it issues, in this same slice, or an explicit `arrow` to one elsewhere |
368
+ | **command has nothing that triggers it** | Add the `ui` it's issued from — or, if the system issues it, the reaction that triggers it, also in this slice. Ask *"who does this, and where are they when they do it?"* |
327
369
  | **read model has no consumer** | Add the screen that displays it, or the reaction that watches it. If it's a repeat added only to shorten an arrow and nothing looks at it there, drop the instance |
328
370
  | read model has no source | Add the missing `from "Event"` |
329
371
  | command produces no event | Give the command its event, or drop the command |
@@ -337,15 +379,17 @@ relevant element over a `# TBD` comment — it shows up as a red marker on the r
337
379
  `em validate` tracks it as an open-issue warning, so it isn't lost once rendered. `em validate
338
380
  --list-issues` gives a quick sweep of everything still open.
339
381
 
340
- Then do one check `em validate` can't: scan every `translation`/`processor`/`automation` slice and
341
- confirm none contains an `event` — each reaction must trigger a `command` in the next slice
342
- (`reaction → command → event`). If you find a reaction wired straight to an event, split it into
343
- two slices and route it through a command.
382
+ `em validate` now catches a reaction wired straight to an event too — any reaction with no
383
+ `command` in its own slice, and no explicit arrow to one, warns "triggers no command". A
384
+ `translation`/`processor`/`automation` should always trigger a `command` in its own slice
385
+ (`reaction → command → event`); if one instead records an event directly, route it through a
386
+ command instead.
344
387
 
345
388
  ---
346
389
 
347
390
  ## em command reference (quick)
348
391
 
392
+ <!-- GENERATED:cli-quick:start -- run `npm run docs:generate` to refresh, do not hand-edit -->
349
393
  ```bash
350
394
  em --version
351
395
  em init <name>.em # optional starter scaffold
@@ -357,6 +401,7 @@ em watch <name>.em -o <name>.svg # re-render on save (run in backgroun
357
401
  em watch <name>.em -o <name>.svg --serve # + live viewer with instant push-reload (--port N)
358
402
  # click "Review mode" for a slice-by-slice storyboard walkthrough
359
403
  ```
404
+ <!-- GENERATED:cli-quick:end -->
360
405
 
361
406
  Always finish a working session by: re-rendering, running `em validate`, and updating
362
407
  `.event-modeling.md` with the current phase/step, decisions, open questions, and a Usage log
@@ -34,10 +34,11 @@ difference is what you do with what you find: extract *builds* a model from the
34
34
 
35
35
  1. A canonical `.em` model with a state file (`.event-modeling.md`), reachable the same way the
36
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.
37
+ 2. The slices you're about to check should be **implemented** — docs at `status: implemented`
38
+ (or the status the user says corresponds to shipped code) for whichever slices are in scope.
39
+ Checking a `draft` or `ready-to-implement` slice against code is a wasted walk — ratified or
40
+ not, nobody has built to it yet; tell the user and skip it rather than reporting "drift"
41
+ against it.
41
42
  3. The codebase to check: the state file's **Existing system refs** (populated during
42
43
  `extract`, if this model came from one) or ask the user for the repo path if not set. Record
43
44
  it if missing. In a headless/scheduled run with no user to ask, default to the repository
@@ -56,7 +57,7 @@ named invariants, Given/When/Then scenarios). Conform checks all three surfaces
56
57
  |---|---|---|---|
57
58
  | **Structural** — `.em` ↔ code | slices/elements/fields/flow | `em` (deterministic) | `em diff --json` on canonical vs. an as-is scratch model |
58
59
  | **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
+ | **Internal** — slice doc ↔ `.em` | the doc's frontmatter (`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
 
61
62
  The internal surface is deterministically checkable in principle (structured doc sections +
62
63
  `{ fields }` blocks) — promoting it to an `em validate` rule is a natural follow-up; file it as
@@ -74,7 +75,7 @@ authoritative.
74
75
  Default: **diff-scoped.** Read the state file's `Last conformance:` marker (date + target-repo
75
76
  revision). If it's set, run `git diff --name-only <recorded-revision>..HEAD` from the target
76
77
  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
+ map those paths to slices (via each slice doc's known code locations, `implementedIn:` links,
78
79
  or a quick Grep if those are stale) — that's your in-scope set. If the marker reads `never`
79
80
  (first run) or the user passes `--full`, scope is every `implemented` slice.
80
81
 
@@ -112,6 +113,18 @@ For each slice in scope:
112
113
  3). If it does, this is not a fresh finding to litigate: classify it as **Accepted
113
114
  divergence** (step 4) and cite the annotation text as the evidence. This is the specific,
114
115
  per-element, ratified counterpart to the idiom-level bullet above.
116
+ - **Unpropagated deltas (MIL-85):** before treating any slice-level finding as drift, check
117
+ that slice's `doc.driftSignal` in `em export --json` (see `docs/slice-doc-schema.md`'s
118
+ "`status` under re-ratification" section for the mechanics). `"unpropagated-delta"` means
119
+ the slice was re-ratified (a new
120
+ `version`, `status` flipped back off `implemented`) but `implementedIn` still names the
121
+ *prior* version's PR — the code you're evidence-walking is that prior version, on purpose.
122
+ Do not diff it against the newer doc content and do not report a mismatch as Real drift or
123
+ a Model gap; classify it as **Unpropagated delta** (step 4) instead and stop — the finding
124
+ is "this delta hasn't shipped yet," not a defect in the shipped code. `"implemented-
125
+ without-link"` (status: implemented, no link at all) is a genuine coherence problem, but
126
+ it's already surfaced by `em validate`'s `frontmatter-coherence-implemented-without-link`
127
+ warning — don't re-raise it here as a separate conform finding.
115
128
  3. Write what you found into the scratch model (`<model-name>-asis.em`, see below). **Seed the
116
129
  scratch model as a copy of the canonical `.em` and replace only the in-scope slices** with
117
130
  the as-is picture, leaving out-of-scope slices byte-identical. This is not an optimization:
@@ -178,6 +191,12 @@ step 2 — sorts into exactly one class:
178
191
  `.em`/`em export` for a spec/internal finding). This is what the annotation is *for*: a
179
192
  reasoned, ratified deviation stops being re-reported as drift on every run. Cite the
180
193
  annotation text, not fresh judgment — that judgment already happened when it was written.
194
+ - **Unpropagated delta** (MIL-85) — the slice's `doc.driftSignal` (`em export --json`) is
195
+ `"unpropagated-delta"`: a ratified version bump hasn't shipped yet, so `implementedIn` still
196
+ names prior work on purpose. Distinct from Accepted divergence — this is a structurally-
197
+ derived, deterministic signal (`status`/`implementedIn` coherence), not a human-authored
198
+ `divergence` annotation; keep the two separate so a reader can tell which mechanism produced
199
+ which finding. Cite `slice.doc.driftSignal` and `slice.doc.version` together as evidence.
181
200
  - **Extraction uncertainty** — no evidence either way. Never reported as drift or a gap.
182
201
 
183
202
  Every classification must cite the evidence recorded in step 2 (or the `em diff` entry, for
@@ -203,8 +222,11 @@ End of run: update the state file's `Last conformance:` marker, exact format:
203
222
  ```
204
223
 
205
224
  The revision is the one you just diffed against, so the next run's scope starts from here —
206
- step 1 parses this line, so keep the format exact. If any proposals were ratified and applied,
207
- log a Decisions entry noting what changed and why.
225
+ step 1 parses this line, so keep the format exact. **The marker only advances with the human
226
+ in the loop**: in an interactive session, update it after walking the report with the user; a
227
+ headless/scheduled run writes the report but never the marker (nobody ratified the outcome),
228
+ so the next run re-walks the same span — see `docs/ci.md`. If any proposals were ratified and
229
+ applied, log a Decisions entry noting what changed and why.
208
230
 
209
231
  ## Conventions
210
232