@milehimikey/em 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/.claude/agents/em-critic.md +26 -0
  2. package/.claude/agents/em-implementer.md +34 -0
  3. package/.claude/agents/em-reviewer.md +31 -0
  4. package/.claude/agents/em-validator.md +27 -0
  5. package/.claude/skills/event-modeling/SKILL.md +52 -12
  6. package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
  7. package/.claude/skills/event-modeling-conform/reference/conform.md +1 -1
  8. package/.claude/skills/event-modeling-design/SKILL.md +140 -18
  9. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  10. package/.claude/skills/event-modeling-engagement/SKILL.md +192 -0
  11. package/.claude/skills/event-modeling-engagement/reference/engagement.md +143 -0
  12. package/.claude/skills/event-modeling-implement/SKILL.md +7 -4
  13. package/.claude/skills/event-modeling-implement/reference/implement.md +51 -12
  14. package/.claude/skills/event-modeling-review/SKILL.md +1 -1
  15. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +274 -188
  16. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +61 -0
  17. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +116 -4
  18. package/.claude/skills/event-modeling-shared/templates/constitution.md +18 -1
  19. package/.claude/skills/event-modeling-shared/templates/slice.md +64 -39
  20. package/.claude/skills/event-modeling-shared/templates/state.md +5 -1
  21. package/.claude-plugin/marketplace.json +14 -0
  22. package/README.md +25 -11
  23. package/dist/catalog/apiFirst.d.ts +28 -0
  24. package/dist/catalog/apiFirst.js +49 -0
  25. package/dist/catalog/apiFirst.js.map +1 -0
  26. package/dist/catalog/docJoin.d.ts +13 -0
  27. package/dist/catalog/docJoin.js +11 -0
  28. package/dist/catalog/docJoin.js.map +1 -1
  29. package/dist/catalog/docModelConsistencyValidate.js +44 -4
  30. package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
  31. package/dist/catalog/invariantsValidate.d.ts +4 -0
  32. package/dist/catalog/invariantsValidate.js +102 -0
  33. package/dist/catalog/invariantsValidate.js.map +1 -0
  34. package/dist/catalog/scenarios.d.ts +15 -0
  35. package/dist/catalog/scenarios.js +97 -0
  36. package/dist/catalog/scenarios.js.map +1 -0
  37. package/dist/catalog/sliceDoc.d.ts +8 -0
  38. package/dist/catalog/sliceDoc.js +9 -0
  39. package/dist/catalog/sliceDoc.js.map +1 -1
  40. package/dist/catalog/sliceReadyValidate.d.ts +31 -4
  41. package/dist/catalog/sliceReadyValidate.js +66 -8
  42. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  43. package/dist/catalog/sliceSections.d.ts +55 -0
  44. package/dist/catalog/sliceSections.js +317 -0
  45. package/dist/catalog/sliceSections.js.map +1 -0
  46. package/dist/cli/agentsMd.js +7 -1
  47. package/dist/cli/agentsMd.js.map +1 -1
  48. package/dist/cli/api.d.ts +90 -0
  49. package/dist/cli/api.js +318 -0
  50. package/dist/cli/api.js.map +1 -0
  51. package/dist/cli/ciInit.d.ts +88 -3
  52. package/dist/cli/ciInit.js +507 -50
  53. package/dist/cli/ciInit.js.map +1 -1
  54. package/dist/cli/conformScope.d.ts +28 -0
  55. package/dist/cli/conformScope.js +67 -0
  56. package/dist/cli/conformScope.js.map +1 -1
  57. package/dist/cli/coverage.d.ts +25 -2
  58. package/dist/cli/coverage.js +86 -12
  59. package/dist/cli/coverage.js.map +1 -1
  60. package/dist/cli/engagement.d.ts +210 -0
  61. package/dist/cli/engagement.js +444 -0
  62. package/dist/cli/engagement.js.map +1 -0
  63. package/dist/cli/engagementFile.d.ts +86 -0
  64. package/dist/cli/engagementFile.js +255 -0
  65. package/dist/cli/engagementFile.js.map +1 -0
  66. package/dist/cli/metrics.d.ts +10 -0
  67. package/dist/cli/metrics.js +49 -1
  68. package/dist/cli/metrics.js.map +1 -1
  69. package/dist/cli/pluginPin.d.ts +55 -0
  70. package/dist/cli/pluginPin.js +188 -0
  71. package/dist/cli/pluginPin.js.map +1 -0
  72. package/dist/cli/ratify.d.ts +27 -2
  73. package/dist/cli/ratify.js +112 -13
  74. package/dist/cli/ratify.js.map +1 -1
  75. package/dist/cli/reratify.d.ts +11 -4
  76. package/dist/cli/reratify.js +89 -35
  77. package/dist/cli/reratify.js.map +1 -1
  78. package/dist/cli/scopeInputs.d.ts +48 -0
  79. package/dist/cli/scopeInputs.js +176 -0
  80. package/dist/cli/scopeInputs.js.map +1 -0
  81. package/dist/cli/skillCheck.d.ts +15 -1
  82. package/dist/cli/skillCheck.js +22 -1
  83. package/dist/cli/skillCheck.js.map +1 -1
  84. package/dist/cli/skillDirs.d.ts +5 -0
  85. package/dist/cli/skillDirs.js +13 -1
  86. package/dist/cli/skillDirs.js.map +1 -1
  87. package/dist/cli/skillSync.d.ts +4 -0
  88. package/dist/cli/skillSync.js +23 -0
  89. package/dist/cli/skillSync.js.map +1 -1
  90. package/dist/cli/sliceNew.d.ts +11 -7
  91. package/dist/cli/sliceNew.js +91 -18
  92. package/dist/cli/sliceNew.js.map +1 -1
  93. package/dist/cli/sliceSync.d.ts +50 -0
  94. package/dist/cli/sliceSync.js +170 -0
  95. package/dist/cli/sliceSync.js.map +1 -0
  96. package/dist/cli/status.d.ts +24 -1
  97. package/dist/cli/status.js +20 -1
  98. package/dist/cli/status.js.map +1 -1
  99. package/dist/cli/systemInputs.d.ts +53 -10
  100. package/dist/cli/systemInputs.js +186 -11
  101. package/dist/cli/systemInputs.js.map +1 -1
  102. package/dist/cli/upgrade.d.ts +30 -7
  103. package/dist/cli/upgrade.js +434 -50
  104. package/dist/cli/upgrade.js.map +1 -1
  105. package/dist/cli/usageLog.d.ts +4 -2
  106. package/dist/cli/usageLog.js +4 -2
  107. package/dist/cli/usageLog.js.map +1 -1
  108. package/dist/cli.js +631 -100
  109. package/dist/cli.js.map +1 -1
  110. package/dist/emit/api.d.ts +29 -0
  111. package/dist/emit/api.js +220 -0
  112. package/dist/emit/api.js.map +1 -0
  113. package/dist/emit/apiCheckJson.d.ts +3 -0
  114. package/dist/emit/apiCheckJson.js +21 -0
  115. package/dist/emit/apiCheckJson.js.map +1 -0
  116. package/dist/emit/codeownersJson.d.ts +3 -0
  117. package/dist/emit/codeownersJson.js +19 -0
  118. package/dist/emit/codeownersJson.js.map +1 -0
  119. package/dist/emit/coverageJson.d.ts +5 -2
  120. package/dist/emit/coverageJson.js +6 -2
  121. package/dist/emit/coverageJson.js.map +1 -1
  122. package/dist/emit/diffJson.d.ts +1 -1
  123. package/dist/emit/diffJson.js +8 -1
  124. package/dist/emit/diffJson.js.map +1 -1
  125. package/dist/emit/engagementJson.d.ts +8 -0
  126. package/dist/emit/engagementJson.js +46 -0
  127. package/dist/emit/engagementJson.js.map +1 -0
  128. package/dist/emit/glossaryJson.d.ts +1 -1
  129. package/dist/emit/glossaryJson.js +4 -1
  130. package/dist/emit/glossaryJson.js.map +1 -1
  131. package/dist/emit/json.d.ts +17 -1
  132. package/dist/emit/json.js +30 -1
  133. package/dist/emit/json.js.map +1 -1
  134. package/dist/emit/metricsJson.d.ts +1 -1
  135. package/dist/emit/metricsJson.js +4 -1
  136. package/dist/emit/metricsJson.js.map +1 -1
  137. package/dist/emit/queryJson.d.ts +1 -1
  138. package/dist/emit/queryJson.js +7 -1
  139. package/dist/emit/queryJson.js.map +1 -1
  140. package/dist/emit/scopeJson.d.ts +12 -0
  141. package/dist/emit/scopeJson.js +26 -0
  142. package/dist/emit/scopeJson.js.map +1 -0
  143. package/dist/emit/skillCheckJson.d.ts +3 -2
  144. package/dist/emit/skillCheckJson.js +10 -2
  145. package/dist/emit/skillCheckJson.js.map +1 -1
  146. package/dist/emit/statusJson.d.ts +1 -1
  147. package/dist/emit/statusJson.js +15 -1
  148. package/dist/emit/statusJson.js.map +1 -1
  149. package/dist/emit/systemJson.d.ts +15 -8
  150. package/dist/emit/systemJson.js +28 -13
  151. package/dist/emit/systemJson.js.map +1 -1
  152. package/dist/emit/upgradeJson.d.ts +1 -1
  153. package/dist/emit/upgradeJson.js +4 -1
  154. package/dist/emit/upgradeJson.js.map +1 -1
  155. package/dist/emit/validateJson.d.ts +3 -3
  156. package/dist/emit/validateJson.js +10 -4
  157. package/dist/emit/validateJson.js.map +1 -1
  158. package/dist/mcp/server.d.ts +1 -1
  159. package/dist/mcp/server.js +186 -37
  160. package/dist/mcp/server.js.map +1 -1
  161. package/dist/model/diff.d.ts +4 -1
  162. package/dist/model/diff.js +44 -8
  163. package/dist/model/diff.js.map +1 -1
  164. package/dist/model/glossary.d.ts +2 -0
  165. package/dist/model/glossary.js +1 -0
  166. package/dist/model/glossary.js.map +1 -1
  167. package/dist/model/model.d.ts +55 -2
  168. package/dist/model/model.js +57 -0
  169. package/dist/model/model.js.map +1 -1
  170. package/dist/model/qualifiedRef.d.ts +18 -0
  171. package/dist/model/qualifiedRef.js +26 -0
  172. package/dist/model/qualifiedRef.js.map +1 -1
  173. package/dist/model/queryIndex.d.ts +9 -1
  174. package/dist/model/queryIndex.js +18 -2
  175. package/dist/model/queryIndex.js.map +1 -1
  176. package/dist/model/rules.d.ts +143 -2
  177. package/dist/model/rules.js +162 -3
  178. package/dist/model/rules.js.map +1 -1
  179. package/dist/model/validate.d.ts +40 -0
  180. package/dist/model/validate.js +147 -1
  181. package/dist/model/validate.js.map +1 -1
  182. package/dist/parser/ast.d.ts +31 -3
  183. package/dist/parser/parser.js +148 -14
  184. package/dist/parser/parser.js.map +1 -1
  185. package/dist/query/format.js +1 -1
  186. package/dist/query/format.js.map +1 -1
  187. package/dist/query/verbs.d.ts +6 -0
  188. package/dist/query/verbs.js +6 -2
  189. package/dist/query/verbs.js.map +1 -1
  190. package/dist/system/adaptation.d.ts +14 -0
  191. package/dist/system/adaptation.js +49 -0
  192. package/dist/system/adaptation.js.map +1 -0
  193. package/dist/system/codeowners.d.ts +77 -0
  194. package/dist/system/codeowners.js +242 -0
  195. package/dist/system/codeowners.js.map +1 -0
  196. package/dist/system/manifest.d.ts +10 -5
  197. package/dist/system/manifest.js +54 -26
  198. package/dist/system/manifest.js.map +1 -1
  199. package/dist/system/migrateManifest.d.ts +28 -0
  200. package/dist/system/migrateManifest.js +155 -0
  201. package/dist/system/migrateManifest.js.map +1 -0
  202. package/dist/system/scope.d.ts +46 -0
  203. package/dist/system/scope.js +132 -0
  204. package/dist/system/scope.js.map +1 -0
  205. package/dist/system/verify.d.ts +50 -13
  206. package/dist/system/verify.js +179 -58
  207. package/dist/system/verify.js.map +1 -1
  208. package/dist/templates.d.ts +2 -2
  209. package/dist/templates.js +23 -2
  210. package/dist/templates.js.map +1 -1
  211. package/package.json +6 -3
  212. package/plugin/.claude-plugin/plugin.json +18 -0
  213. package/plugin/agents/em-critic.md +27 -0
  214. package/plugin/agents/em-implementer.md +35 -0
  215. package/plugin/agents/em-reviewer.md +32 -0
  216. package/plugin/agents/em-validator.md +28 -0
  217. package/plugin/shared/reference/em-dsl.md +866 -0
  218. package/plugin/shared/reference/methodology.md +329 -0
  219. package/plugin/shared/reference/operating-principles.md +222 -0
  220. package/plugin/shared/reference/slice-doc-schema.md +566 -0
  221. package/plugin/shared/templates/conformance-report.md +85 -0
  222. package/plugin/shared/templates/constitution.md +136 -0
  223. package/plugin/shared/templates/model-readme.md +46 -0
  224. package/plugin/shared/templates/slice.md +251 -0
  225. package/plugin/shared/templates/state.md +77 -0
  226. package/plugin/skills/conform/SKILL.md +93 -0
  227. package/plugin/skills/conform/reference/conform.md +428 -0
  228. package/plugin/skills/design/SKILL.md +297 -0
  229. package/plugin/skills/discover/SKILL.md +90 -0
  230. package/plugin/skills/discover/reference/extract.md +147 -0
  231. package/plugin/skills/engagement/SKILL.md +193 -0
  232. package/plugin/skills/engagement/reference/engagement.md +144 -0
  233. package/plugin/skills/event-modeling/SKILL.md +132 -0
  234. package/plugin/skills/implement/SKILL.md +61 -0
  235. package/plugin/skills/implement/reference/implement.md +439 -0
  236. package/plugin/skills/review/SKILL.md +74 -0
  237. package/dist/emit/typespec.d.ts +0 -26
  238. package/dist/emit/typespec.js +0 -356
  239. package/dist/emit/typespec.js.map +0 -1
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: conform
3
+ em-version: 1.14.0
4
+ description: >-
5
+ Use when checking a ratified event model (and its slice docs) for drift against the codebase
6
+ that implements it — advisory only, never a gate, never an unprompted edit — or when walking
7
+ `em validate` diagnostics on a model to explain and fix rule violations. Drives `em`'s conform
8
+ and validate phases.
9
+ ---
10
+ <!-- GENERATED FILE from .claude/skills/event-modeling-conform/SKILL.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
11
+
12
+ # Event Modeling — conform & validate
13
+
14
+ ## Phase: `conform` — drift check against the codebase
15
+
16
+ Goal: check a **ratified** model (and its slice docs) against the codebase that implements it,
17
+ and report where they've drifted — advisory only, never a gate, never an unprompted edit.
18
+
19
+ Preconditions (tool check, model location) are the same as every other phase — see
20
+ `${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md`.
21
+
22
+ **Read `reference/conform.md` before doing any conform work** — it carries the full flow
23
+ (scope, evidence-first verification, `em diff --json`, classification, report) and the
24
+ stance guardrails (evidence-first, uncertainty is never drift, propose-don't-edit). It reuses
25
+ the `em:discover` skill's `extract` sourcing/mode rules
26
+ (`${CLAUDE_PLUGIN_ROOT}/skills/discover/reference/extract.md`) for reading the target codebase rather than
27
+ duplicating them.
28
+
29
+ In short: run `em conform-scope <model-name>.em --repo <target-repo-path> --seed-asis` to
30
+ compute the in-scope slice set (diff-scoped by default, via `Last conformance:` + the target
31
+ repo's changed paths mapped through each slice doc's `implementedIn:`) and seed the scratch
32
+ model in one step; for each in-scope slice, gather code evidence *before* comparing to the model
33
+ or doc; write the as-is picture into that scratch model (`<model-name>-asis.em`, reusing the
34
+ canonical model's names wherever the code matches them); run `em diff <model-name>.em
35
+ <model-name>-asis.em --json` and let `em` decide the structural deltas; classify every finding
36
+ (real drift / model gap / internal inconsistency / uncertainty) with cited evidence; write
37
+ `conformance/<date>-report.md` **and its structured twin `conformance/<date>-findings.json`**
38
+ (one entry per finding, `em conform-findings check <path>` to verify) with proposed `issue
39
+ "conformance: …"` red notes; apply only the proposals the user ratifies, then re-render and
40
+ validate; run `em state set-conformance <revision> --report <path>` to update the state file's
41
+ `Last conformance:` marker.
42
+
43
+ Ruling on a finding is a human gate, like ratifying a slice
44
+ (docs/process.md#the-slice-lifecycle-gates): the sweep gathers evidence and proposes, a person
45
+ decides, and `em conform-supersede ... --locus <model|doc|code|none> --by <name>` records the
46
+ ruling on the findings record (and stamps the report) — never rule on your own judgment. Once
47
+ every finding touching a slice is ruled, `em slice conform <model-name>.em <key> --at <rev>`
48
+ certifies that version of the slice.
49
+
50
+ End of phase: state file's `Last conformance:` marker updated (via `em state set-conformance` —
51
+ refuses while any `implemented` slice still has an unruled finding; `--partial` escapes with a
52
+ loud notice), Decisions log entry if any proposals were applied. Conform doesn't chain to
53
+ another phase — it's a recurring loop, run again whenever the codebase has moved.
54
+
55
+ A full (non-`--partial`) `em state set-conformance` also **certifies the model's current design
56
+ version** (MIL-218, `docs/process.md#model-versions`) — it refuses if no design version has ever
57
+ been bumped ("bump a model version first") or if the model changed during this run without a
58
+ re-bump; run `em model version bump <model-name>.em --by <name>` before the final `em state
59
+ set-conformance` call if either applies.
60
+
61
+ ## Phase: `validate`
62
+
63
+ Run `em validate <model-name>.em` and walk through each diagnostic with the user, explaining the
64
+ rule (see `${CLAUDE_PLUGIN_ROOT}/shared/reference/em-dsl.md`) and proposing the fix. Apply fixes on
65
+ agreement. Common ones:
66
+
67
+ | Diagnostic | Fix |
68
+ |---|---|
69
+ | reaction triggers no command | Add the command it issues, in this same slice, or an explicit `arrow` to one elsewhere |
70
+ | **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?"* |
71
+ | **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 |
72
+ | read model has no source | Add the missing `from "Event"` |
73
+ | command produces no event | Give the command its event, or drop the command |
74
+ | **event is not read by any read model** | Add the read slice that projects it — but ask *who looks at this and what do they do with it* first. "Nobody" is a real answer, and then the question is why it's recorded at all |
75
+ | **illegal `arrow` kind pair** | Don't reroute it — the model is missing an element. `command → view` needs the event between them; `view → command` needs a reaction. Between two instances of one read model, just delete the arrow: the repeat needs no connection |
76
+ | `view X again` with no earlier declaration | Declare the view plainly the first time it appears |
77
+ | event feeding an earlier view instance | Add a `view X again` where the event lands and move the source there |
78
+
79
+ For anything genuinely unresolved rather than a rule violation, prefer `issue "text"` on the
80
+ relevant element over a `# TBD` comment — it shows up as a red marker on the rendered diagram and
81
+ `em validate` tracks it as an open-issue warning, so it isn't lost once rendered. `em validate
82
+ --list-issues` gives a quick sweep of everything still open.
83
+
84
+ `em validate` now catches a reaction wired straight to an event too — any reaction with no
85
+ `command` in its own slice, and no explicit arrow to one, warns "triggers no command". A
86
+ `translation`/`processor`/`automation` should always trigger a `command` in its own slice
87
+ (`reaction → command → event`); if one instead records an event directly, route it through a
88
+ command instead.
89
+
90
+ Validate isn't pinned to this phase alone — every other phase runs it continuously as part of
91
+ the shared operating principles (`${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md`).
92
+ This phase is for a dedicated, focused walkthrough of whatever diagnostics remain, and for the
93
+ full rule catalog in `${CLAUDE_PLUGIN_ROOT}/shared/reference/em-dsl.md`.
@@ -0,0 +1,428 @@
1
+ <!-- GENERATED FILE from .claude/skills/event-modeling-conform/reference/conform.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
2
+ # Conform Phase — Drift Detection Against the Codebase
3
+
4
+ Read this **before doing any conform work**. It governs the `conform` phase: checking a
5
+ **ratified** model (and its slice docs) against the codebase that is supposed to implement it,
6
+ and reporting where they've drifted apart. Conform reuses `extract`'s sourcing/mode rules
7
+ (the `em:discover` skill's `reference/extract.md`) for reading the target codebase
8
+ rather than duplicating them — the
9
+ difference is what you do with what you find: extract *builds* a model from the code; conform
10
+ *compares* code evidence against a model that already exists.
11
+
12
+ ## Stance — advisory-only, evidence-first
13
+
14
+ - **Advisory-only.** Conform never gates anything and never edits the canonical model or a
15
+ slice doc on its own initiative. It proposes; the user ratifies. There is no such thing as
16
+ an auto-applied conformance fix.
17
+ - **Evidence-first, not model-first.** For every slice you check, gather code evidence —
18
+ handler/event/projection/endpoint sites, invariant-enforcement sites, scenario-test sites —
19
+ **before** you compare anything to the model or doc. Read the doc and model first to know
20
+ *what to look for*, but write down what the code actually shows before you judge whether it
21
+ matches. Anchoring bias is the failure mode this rule exists to block: "the model says X" is
22
+ never allowed to substitute for "the code shows X at `path/to/File.kt:42`".
23
+ - **Uncertainty is never drift.** If you can't find evidence either way — the code path is
24
+ unclear, the test is ambiguous, you ran out of budget — that's an **uncertainty**, reported
25
+ in its own section, never folded into a drift or gap finding. A false positive here is worse
26
+ than a missed one; it's what burns the loop's credibility.
27
+ - **Anchored, not blind.** You read the canonical model's vocabulary before walking the code,
28
+ and you reuse its slice/element names wherever the code genuinely matches them. This trades
29
+ some independence for precision: naming a scratch model's elements from scratch (blind
30
+ extraction) would report wholesale slice add/remove noise from naming alone the moment your
31
+ synonym for an existing element doesn't match the model's — the exact false-drift failure
32
+ mode this phase exists to avoid. The guardrail against anchoring bias is the evidence-first
33
+ rule above, not independence from the model's vocabulary.
34
+
35
+ ## Preconditions
36
+
37
+ 1. A canonical `.em` model with a state file (`.event-modeling.md`), reachable the same way the
38
+ shared preconditions locate it — see `../../../shared/reference/operating-principles.md`.
39
+ 2. The slices you're about to check should be **implemented** — docs at `status: implemented`
40
+ (or the status the user says corresponds to shipped code) for whichever slices are in scope.
41
+ Checking a `draft` or `ready-to-implement` slice against code is a wasted walk — ratified or
42
+ not, nobody has built to it yet; tell the user and skip it rather than reporting "drift"
43
+ against it.
44
+ 3. The codebase to check: the state file's **Existing system refs** (populated during
45
+ `extract`, if this model came from one) or ask the user for the repo path if not set. Record
46
+ it if missing. In a headless/scheduled run with no user to ask, default to the repository
47
+ the model lives in (if it contains the implementation) and state that assumption in the
48
+ report's run metadata.
49
+ 4. Confirm `em --version` works (same check as every other phase) — you'll run `em diff`. The
50
+ phase needs `em diff --json`; if the flag errors as unknown, the installed `em` predates it
51
+ — say so and stop rather than falling back to eyeballing the two models.
52
+
53
+ ## The three conformance surfaces
54
+
55
+ The model isn't just the `.em` — slice docs carry the detailed spec (field tables with rules,
56
+ named invariants, Given/When/Then scenarios). Conform checks all three surfaces that can drift:
57
+
58
+ | Surface | What drifts | Who checks | How |
59
+ |---|---|---|---|
60
+ | **Structural** — `.em` ↔ code | slices/elements/fields/flow | `em` (deterministic) | `em diff --json` on canonical vs. an as-is scratch model |
61
+ | **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) |
62
+ | **Internal** — slice doc ↔ `.em` | the doc's frontmatter `pattern:` or its Command/Event/Read Model sections and field tables disagreeing with the model's own elements/fields | `em` (deterministic) | `em validate`'s `doc-model-*` diagnostics, unconditionally in the default list |
63
+
64
+ The internal surface is deterministically checked by `em validate` itself (MIL-124) — four
65
+ diagnostics, always on, no flag needed: `doc-model-pattern-mismatch` (frontmatter `pattern:` vs.
66
+ the model's own classification), `doc-model-element-not-in-model` / `doc-model-element-not-in-doc`
67
+ (the doc's Command/Event/Read Model markers vs. the slice's actual elements, each direction), and
68
+ `doc-model-field-mismatch` (a matched Command/Event's field table vs. the model's `{ fields }`
69
+ block). Run `em validate <model-name>.em --json` and read each entry's `code`/`refs`/`message`
70
+ (`docs/validation.md#doc-model-consistency` documents exactly what each one checks and
71
+ when it stays silent — every rule is declare-gated, so a thin/draft doc doesn't nag); consume it
72
+ the same way step 3 below consumes `em diff --json` — parse the structured findings, don't
73
+ re-derive them by eye. Fold any that fire for an in-scope slice into that slice's internal-surface
74
+ findings for step 4's classification, citing the diagnostic directly as the evidence.
75
+
76
+ **Internal inconsistency is its own class, not drift.** When a doc and the `.em` disagree with
77
+ each other, neither one is "the code" — you don't know which is stale. Report it as internal
78
+ inconsistency and let the user decide which side is right; don't guess by treating one as
79
+ authoritative.
80
+
81
+ ## Flow
82
+
83
+ ### 1. Scope
84
+
85
+ Run `em conform-scope <model-name>.em --repo <target-repo-path>` — it mechanizes everything
86
+ about scoping that's deterministic: parsing `Last conformance:` (via the same state-file reader
87
+ `em state read` uses), running `git diff --name-only <revision>..HEAD` in the target repo, and
88
+ mapping each changed path to a slice via that slice doc's `implementedIn:` link (a bare/embedded
89
+ repo-relative path that's a prefix of or equal to the changed path — a URL-only `implementedIn`,
90
+ e.g. a PR link, deterministically matches nothing). It prints one JSON document
91
+ (`docs/cli.md`'s conform-scope section documents the envelope):
92
+
93
+ ```json
94
+ { "lastConformance": { "date": "...", "revision": "..." } | null,
95
+ "changedPaths": ["..."],
96
+ "candidateSlices": [ { "key": "checkout", "matchedBy": "implementedIn", "paths": ["..."] } ],
97
+ "unmappedPaths": ["..."] }
98
+ ```
99
+
100
+ Default: **diff-scoped** — `lastConformance` set, `--full` not passed — `candidateSlices` is
101
+ your in-scope set for step 2. If `lastConformance` is `null` (first run) or you pass `--full`,
102
+ `em conform-scope` scopes every `implemented` slice instead (`matchedBy: "full"`,
103
+ `changedPaths`/`unmappedPaths` both empty) — the tool makes this call for you, no need to
104
+ special-case it yourself.
105
+
106
+ **`unmappedPaths` is your judgment queue, not a dead end.** A changed path the tool couldn't
107
+ place mechanically (`implementedIn` stale, a URL-only link, code that moved) still might belong
108
+ to an in-scope slice — a quick Grep for the path's obvious symbols against the slice docs/model
109
+ vocabulary is exactly the judgment call this phase reserves for you (`em conform-scope` never
110
+ guesses on purpose). Fold anything you place this way into your in-scope set alongside
111
+ `candidateSlices`; leave genuinely unplaceable paths out of scope but note them in the report's
112
+ run metadata rather than silently dropping them.
113
+
114
+ Cost containment here is cadence, not a trigger condition — diff-scoped-by-default is what
115
+ keeps a recurring conform run cheap; don't invent a smarter trigger.
116
+
117
+ ### 2. Anchored, evidence-first verification (per in-scope slice)
118
+
119
+ For each slice in scope:
120
+
121
+ 1. Read the slice's **doc** (`slices/<slice-name>.md`) alongside its elements in the `.em`.
122
+ This tells you the vocabulary to anchor on and the claims to check — not the answer.
123
+ 2. Walk the code and **record evidence before comparing**:
124
+ - **Structural:** the handler, event class(es), projection(s), endpoint(s) that implement
125
+ this slice.
126
+ - **Spec:** for every named invariant (`INV-*`) in the doc, its enforcement site and its
127
+ test site (by name/path, both or neither — a half-found invariant is an uncertainty, not
128
+ a pass). Scenarios are budgeted: map Given/When/Then scenarios to individual test sites
129
+ when the in-scope slice count is small (roughly ≤5) or the user asks for that depth;
130
+ otherwise record slice-level scenario-test presence and say so in the report — per-scenario
131
+ mapping across a full model roughly doubles the walk. Where the doc doesn't name
132
+ invariants/scenarios individually, fall back to slice-level test presence (some vs. none)
133
+ rather than inventing IDs the doc never gave you.
134
+ - **Legitimate non-materialized views:** an automation's watched view may have no
135
+ materialized projection in code — a direct event handler with criteria-sourced state is
136
+ the same pattern expressed in the code idiom (extract.md's R5 reaction rules). That is
137
+ NOT structural drift; note the idiom in the evidence log instead of reporting the view
138
+ as missing. This is the *general*, idiom-level version of the next bullet — prefer citing
139
+ an actual `divergence` annotation when the element carries one; fall back to this idiom
140
+ note only when it doesn't.
141
+ - **Accepted divergences:** before treating any finding as drift, check whether the
142
+ relevant canonical element already carries a `divergence "..."` annotation (visible in
143
+ the `.em`, in `em export --json`'s `divergence` field, or — for a structural finding — as
144
+ the `acceptedDivergence` field already attached to that `em diff --json` entry, see step
145
+ 3). If it does, this is not a fresh finding to litigate: classify it as **Accepted
146
+ divergence** (step 4) and cite the annotation text as the evidence. This is the specific,
147
+ per-element, ratified counterpart to the idiom-level bullet above.
148
+ - **Unpropagated deltas (MIL-85):** before treating any slice-level finding as drift, check
149
+ that slice's `doc.driftSignal` in `em export --json` (see `../../../shared/reference/slice-doc-schema.md`'s
150
+ "`status` under re-ratification" section for the mechanics). `"unpropagated-delta"` means
151
+ the slice was re-ratified (a new
152
+ `version`, `status` flipped back off `implemented`) but `implementedIn` still names the
153
+ *prior* version's PR — the code you're evidence-walking is that prior version, on purpose.
154
+ Do not diff it against the newer doc content and do not report a mismatch as Real drift or
155
+ a Model gap; classify it as **Unpropagated delta** (step 4) instead and stop — the finding
156
+ is "this delta hasn't shipped yet," not a defect in the shipped code. `"implemented-
157
+ without-link"` (status: implemented, no link at all) is a genuine coherence problem, but
158
+ it's already surfaced by `em validate`'s `frontmatter-coherence-implemented-without-link`
159
+ warning — don't re-raise it here as a separate conform finding.
160
+ 3. Write what you found into the scratch model (`<model-name>-asis.em`, see below) — start it
161
+ with `em conform-scope <model-name>.em --repo <target-repo-path> --seed-asis`, which
162
+ byte-copies the canonical `.em` to `<model-name>-asis.em` and ensures `*-asis.em` is
163
+ gitignored (idempotent — safe to pass on every run). **Then replace only the in-scope
164
+ slices** with the as-is picture, leaving out-of-scope slices byte-identical. This is not an optimization:
165
+ `em diff` matches slices by name, so a scratch model containing *only* the in-scope slices
166
+ reports every other canonical slice as `slice-removed` plus one `element-removed` per
167
+ element — a phantom-removal flood on every diff-scoped run, which is the exact false drift
168
+ this phase exists to avoid. It also keeps the scratch model compilable (see step 3). Within
169
+ the slices you do rewrite, **reuse the canonical model's slice and element names wherever
170
+ the code genuinely matches them** — the canonical model is the vocabulary anchor. Give a
171
+ new name only to code behavior the model doesn't cover at all. When you do need a new name,
172
+ check `em glossary` across sibling `.em` models first (same aid the `extract` phase uses) —
173
+ if the term already exists elsewhere under a different kind or spelling, that's a signal to
174
+ reuse the established name rather than mint another synonym for the same concept. Record the
175
+ mapping decision for every element you name this way (e.g. `CartItemAdded` code class ↔
176
+ `"Cart Item Added"` model event) so it's auditable, not a silent judgment call. **Match the
177
+ canonical model's field granularity:** declare `{ fields }`
178
+ in the scratch model only where the canonical model declares them (if the canonical model
179
+ puts fields on commands but not events, so does the scratch model — writing code-derived
180
+ event fields there would flood the diff with `field-added` noise). Schema claims at finer
181
+ granularity than the `.em` declares — e.g. event fields documented only in a slice doc's
182
+ table — are checked on the **spec surface** against the doc, not smuggled into the
183
+ structural diff.
184
+ 4. Read the in-scope slice's `doc-model-*` findings straight off the `em validate --json` run
185
+ from precondition 4 (or re-run it here) — no code evidence needed, `em` already did the
186
+ doc-vs-`.em` comparison for frontmatter `pattern:` and the Command/Event/Read Model rosters
187
+ and field tables. The only judgment left on this surface is narrative sections `em validate`
188
+ can't parse (Intent, Open Questions, notes): count a contradiction here too when it's
189
+ unambiguous — a stale sentence that flatly contradicts the `.em` is an internal
190
+ inconsistency; a vague or interpretable one is not a finding.
191
+
192
+ Do this for every in-scope slice before moving on to diffing. The evidence you record here is
193
+ what every finding in the report will cite — file paths, not vibes.
194
+
195
+ ### 3. Deterministic diff
196
+
197
+ First `em validate <model-name>-asis.em`. `em diff` refuses when either side has validation
198
+ *errors* (warnings are fine), so a scratch model that doesn't compile stops the run cold. The
199
+ usual cause is a slice that depends on one you rewrote incompletely — a `view X again` whose
200
+ first declaration you dropped, an automation `from` a read model that no longer exists, or an
201
+ `arrow` you wrote joining kinds the patterns don't connect. (An unread event is only a *warning*,
202
+ so it never blocks the diff — and in an as-is scratch model it's expected.)
203
+ Seeding from the canonical model (step 2) prevents this; if it still fails, **fix the scratch
204
+ model and report the failure as a tooling problem in the run metadata — never as drift.** A
205
+ model you couldn't compile is an uncertainty about your own scratch file, not a finding
206
+ against the codebase.
207
+
208
+ Then run `em diff <model-name>.em <model-name>-asis.em --json`. `em`, not you, decides what
209
+ structurally differs — don't re-derive by eye what the tool already computed. Parse the JSON
210
+ (`docs/cli.md`'s diff section documents the envelope: `diffSchemaVersion`, `counts`, `changes`,
211
+ `removals`, every optional `ChangeEntry` field explicit-`null` when unused) and use it as the
212
+ structural-surface findings directly. Check each entry's `acceptedDivergence` field first: a
213
+ non-null value means `em` itself already found a `divergence` annotation on the canonical
214
+ element behind this entry — that finding is Accepted divergence (step 4), not something to
215
+ re-litigate.
216
+
217
+ ### 4. Classify
218
+
219
+ Every finding — structural deltas from step 3, plus spec- and internal-surface findings from
220
+ step 2 — sorts into exactly one class:
221
+
222
+ - **Real drift** — code evidence contradicts the model or doc.
223
+ - **Model gap** — code evidence shows behavior neither the model nor the doc covers.
224
+ - **Internal inconsistency** — the doc and the `.em` disagree with each other (surface 3 only).
225
+ - **Accepted divergence** — the relevant canonical element carries a `divergence "..."`
226
+ annotation (cited directly from a structural entry's `acceptedDivergence` field, or from the
227
+ `.em`/`em export` for a spec/internal finding). This is what the annotation is *for*: a
228
+ reasoned, ratified deviation stops being re-reported as drift on every run. Cite the
229
+ annotation text, not fresh judgment — that judgment already happened when it was written.
230
+ - **Unpropagated delta** (MIL-85) — the slice's `doc.driftSignal` (`em export --json`) is
231
+ `"unpropagated-delta"`: a ratified version bump hasn't shipped yet, so `implementedIn` still
232
+ names prior work on purpose. Distinct from Accepted divergence — this is a structurally-
233
+ derived, deterministic signal (`status`/`implementedIn` coherence), not a human-authored
234
+ `divergence` annotation; keep the two separate so a reader can tell which mechanism produced
235
+ which finding. Cite `slice.doc.driftSignal` and `slice.doc.version` together as evidence.
236
+ - **Extraction uncertainty** — no evidence either way. Never reported as drift or a gap.
237
+
238
+ **Read-model exposure (MIL-215).** Two rulings apply the classes above to a `view`'s exposure —
239
+ whether it ships a query endpoint — instead of leaving each case to fresh judgment:
240
+
241
+ - An **internal view** (no `ui` reads it, and it isn't marked `public`) implemented as
242
+ criteria-sourced state (a reaction's own read, no materialized projection — the general idiom
243
+ step 2 already names) or as an unexposed projection is **Conformant, never dead code to
244
+ flag**. It's the read operation the model draws to say an automation or translation *must*
245
+ read, not an endpoint promise; don't propose deleting it.
246
+ - An **endpoint in code on a view the model shows no `ui` reading and no `public` marking** is a
247
+ **Model gap** — "add the `ui` or remove the endpoint" — **never Real drift to delete**.
248
+ Nobody decided whether that view is exposed; deleting the code decides it for them.
249
+
250
+ Every classification must cite the evidence recorded in step 2 (or the `em diff` entry, for
251
+ structural findings) — a finding with no citation isn't ready to report.
252
+
253
+ ### 5. Report + findings record + proposals
254
+
255
+ Write `conformance/<YYYY-MM-DD>-report.md` in the model directory, from
256
+ `../../../shared/templates/conformance-report.md`. For a real-drift or model-gap finding, propose a
257
+ ready-to-apply red note — `issue "conformance: <text>"` on the right element, written out in
258
+ the report exactly as it should be pasted into the `.em` — when a model-side marker is the
259
+ right fix; when the fix is purely doc wording (or the finding is an internal inconsistency
260
+ with no clear side to flag), omit the red note and say why instead. **Never propose a red note
261
+ for a finding already classified Accepted divergence** — report it (so the annotation itself
262
+ stays auditable) but don't ask the user to ratify what's already ratified. **You never edit the
263
+ canonical model or a slice doc unprompted** — walk the report with the user, apply only the
264
+ proposals they approve, then re-render and `em validate`.
265
+
266
+ **Also write the findings JSON (MIL-214).** Alongside the report, write
267
+ `conformance/<YYYY-MM-DD>-findings.json` — one entry per `### n.` finding, ids matching the
268
+ report's own numbering. Shape (`findingsSchemaVersion: "1.0"`, additive-only, 2-space indent,
269
+ keys sorted, findings sorted by `id`, trailing newline):
270
+
271
+ ```json
272
+ {
273
+ "findingsSchemaVersion": "1.0",
274
+ "model": "checkout.em",
275
+ "report": "conformance/YYYY-MM-DD-report.md",
276
+ "revision": "<target-repo revision>",
277
+ "findings": [
278
+ {
279
+ "id": 1,
280
+ "surface": "structural",
281
+ "class": "Real drift",
282
+ "slice": "checkout",
283
+ "evidence": "code shows X at path/to/File.kt:42",
284
+ "locus": null,
285
+ "resolvedBy": null,
286
+ "resolvedOn": null
287
+ }
288
+ ]
289
+ }
290
+ ```
291
+
292
+ `surface` is one of `structural` (`.em` <-> code — step 3's diff), `spec` (slice doc <-> code —
293
+ step 2's invariant/scenario walk), `internal` (slice doc <-> `.em` — step 2's `doc-model-*`
294
+ diagnostics), or `other`. `class` is the step-4 classification verbatim (Real drift, Model gap,
295
+ Internal inconsistency, Accepted divergence, Unpropagated delta, Extraction uncertainty). `slice`
296
+ is the concerned slice's export key, or `null` for a finding that isn't attributable to one
297
+ (counts as in-scope for every slice's unruled-findings check). `locus`/`resolvedBy`/`resolvedOn`
298
+ start `null` — a later `em conform-supersede --locus --by` call fills them in once a human rules.
299
+ Run `em conform-findings check conformance/YYYY-MM-DD-findings.json` before ending the session —
300
+ a headless run's mechanical proof it wrote a valid record.
301
+
302
+ **Ruling on a finding (MIL-214) — once the user decides.** Run, per finding or batch of findings
303
+ ruled the same way:
304
+
305
+ ```
306
+ em conform-supersede <model-name>.em conformance/YYYY-MM-DD-report.md --as-of <target-repo revision> \
307
+ --findings <spec> --locus <model|doc|code|none> --by <name>
308
+ ```
309
+
310
+ — this records `locus`/`resolvedBy`/`resolvedOn` on the named findings in the JSON (refusing to
311
+ silently overwrite an already-different `locus`) AND stamps the report's "superseded as of `<rev>`"
312
+ banner in the same call (see below). `locus: code` on an unresolved finding is the carrier for
313
+ "the spec is right, the code is wrong" — no doc delta required for that case any more; `locus:
314
+ model` means fix the `.em` (apply the proposed red note, or a stronger edit the user chose
315
+ instead); `locus: doc` means the slice doc needs a delta/re-ratification; `locus: none` covers
316
+ Accepted divergence/Unpropagated delta/Extraction uncertainty, where nothing needs to change.
317
+
318
+ **Certifying a slice, once every finding touching it is ruled (MIL-214).** For each in-scope
319
+ slice with no unruled findings left:
320
+
321
+ ```
322
+ em slice conform <model-name>.em <slice-key> --at <target-repo revision>
323
+ ```
324
+
325
+ — records `conformedVersion`/`conformedAt`/`conformedOn` on that slice's doc, so
326
+ `driftSignal` reads `in-sync` again (it reads `uncertified` for an implemented slice that's never
327
+ been through this) instead of just trusting `implementedIn`. Legal only for `status: implemented`
328
+ with a link, and itself refuses if an unruled finding for that slice still exists at this
329
+ revision — the mechanical proof this step comes only after rulings, not before.
330
+
331
+ End of run: update the state file's `Last conformance:` marker with
332
+
333
+ ```
334
+ em state set-conformance <target-repo revision> --report conformance/YYYY-MM-DD-report.md
335
+ ```
336
+
337
+ — the revision is the one you just diffed against, so the next run's scope starts from here.
338
+ The command writes the exact format step 1 parses back out; never hand-edit the bullet.
339
+ **Refuses (MIL-214) while any `implemented` slice still has an unruled finding** in the findings
340
+ JSON beside the report — rule on every finding first (or certify what's already ruled via `em
341
+ slice conform` above), or pass `--partial` to record the marker with a loud notice instead
342
+ (`notice: conformance marker recorded as PARTIAL — N finding(s) unruled`) when the team
343
+ deliberately wants to advance the marker without finishing every ruling this session.
344
+ **The marker only advances with the human in the loop**: in an interactive session, run it
345
+ after walking the report with the user; a headless/scheduled run writes the report and findings
346
+ JSON but never the marker (nobody ratified the outcome), so the next run re-walks the same span —
347
+ see `docs/ci.md`. If any proposals were ratified and applied, log a Decisions entry noting what
348
+ changed and why.
349
+
350
+ **Superseding the report you just walked (MIL-164).** The `--locus`/`--by` call above already
351
+ stamps the report's banner in the same call when a ruling was recorded; if a finding's ruling
352
+ doesn't need `--locus`/`--by` recorded (a migration-path report predating the findings JSON, or
353
+ a ruling you're only banner-stamping after the fact), run the same command without them:
354
+
355
+ ```
356
+ em conform-supersede <model-name>.em conformance/YYYY-MM-DD-report.md --as-of <target-repo revision> --findings <spec>
357
+ ```
358
+
359
+ — stamping the report with a "superseded as of `<rev>`" banner so a later reader who finds it
360
+ (a search hit, a bookmark, a link from an old PR description) sees immediately that its
361
+ file:line citations describe an ancestor of the current model, not its current state. This is
362
+ what keeps a superseded report an honest piece of history rather than a silently-misleading
363
+ current view. `<spec>` names the finding number(s) this run's ruling covers (e.g. `"1-3"`); if
364
+ only some findings were ruled on this session, run it again later with the remaining numbers —
365
+ each call adds its own stamp rather than overwriting the last.
366
+
367
+ ## Conventions
368
+
369
+ - **Scratch model:** `<model-name>-asis.em`, written **next to** the canonical model (same
370
+ directory as `.event-modeling.md`). Always seeded as a copy of the canonical model with only
371
+ the in-scope slices rewritten (step 2) — `em conform-scope --seed-asis` does the seeding and
372
+ the gitignoring (`*-asis.em`, idempotent) mechanically, never built up from nothing and never
373
+ hand-maintained. It's regenerated every conform run.
374
+ - **Report location:** `conformance/<YYYY-MM-DD>-report.md` in the model directory. One file
375
+ per run; don't overwrite a prior date's report.
376
+ - **Red note wording:** `issue "conformance: <what's wrong, plainly>"` — plain enough that
377
+ whoever ratifies it later understands the claim without re-reading the report.
378
+ - **Naming/matching rules:** same past-tense-event / imperative-command / thing-shown-view
379
+ naming discipline as the rest of the skill (`methodology.md`'s Socratic stance) — you're
380
+ matching code to existing names here, not inventing new ones except for genuinely uncovered
381
+ behavior.
382
+
383
+ ## Completion & handoff
384
+
385
+ Conform is complete when: every in-scope slice has been walked (step 2), the scratch model has
386
+ been diffed (step 3), every finding is classified with cited evidence (step 4), the report AND
387
+ its findings JSON are written (step 5), the state file's `Last conformance:` marker is updated
388
+ (or deliberately left as `--partial`), and the user has seen the report and said which proposals
389
+ (if any) to apply and which findings resolve to which locus. Applied proposals get re-rendered
390
+ and re-validated before you call the run done.
391
+
392
+ A full (non-`--partial`) `em state set-conformance` does one more thing (MIL-218): it also
393
+ **certifies** the model's current design version — writes `certified: { at, on, report,
394
+ findings }` into that version's `model-versions/v<N>.json` manifest, and rewrites the state
395
+ file's `Certified:` bullet. This is the "conform ran clean against version N" fact a
396
+ stakeholder or a release note can point at. It only works on an already-**bumped** design
397
+ version — `em state set-conformance` refuses (pointing at `em model version bump`) if no
398
+ manifest exists yet, or if the model has drifted since the last bump (the certification would
399
+ otherwise name a version that isn't what conform actually walked). If the model changed during
400
+ this conform run (a slice's `version:` moved, or the `.em` file itself changed), bump a new
401
+ design version (`em model version bump --by <name>`) before the final `em state
402
+ set-conformance` call. `--partial` never certifies. See `docs/process.md#model-versions`.
403
+
404
+ Conform doesn't chain to another phase automatically — it's a recurring loop, not a step in
405
+ building the model. Suggest running it again next time the target codebase has moved, at
406
+ whatever cadence the user wants (see `docs/ci.md` for a scheduled-run recipe).
407
+
408
+ ## Anti-patterns
409
+
410
+ - **Blind extraction** — deriving the as-is scratch model with no sight of the canonical
411
+ model's vocabulary. Loses the anchor and produces naming-driven false drift.
412
+ - **A scratch model holding only the in-scope slices** — `em diff` reads every omitted
413
+ canonical slice as removed, so a diff-scoped run drowns in phantom removals. Seed from the
414
+ canonical model and rewrite only the slices you walked.
415
+ - **Reporting uncertainty as drift** — "I couldn't find the enforcement site" is an
416
+ uncertainty, never a finding against the model.
417
+ - **Editing the model or a slice doc without ratification** — conform proposes; the user
418
+ applies. No exceptions, even for an "obviously correct" fix.
419
+ - **Treating scenario/test absence as a build failure** — a missing test site is a spec-surface
420
+ finding to report and classify, not a reason to stop the run or block anything. Conform is
421
+ advisory; there's no gate here.
422
+ - **Re-deriving what `em diff` already decided** — once you have the JSON, use it; don't
423
+ eyeball the two `.em` files yourself looking for structural differences the tool already
424
+ computed for you.
425
+ - **Re-litigating an accepted divergence** — a non-null `acceptedDivergence` on a diff entry
426
+ (or a `divergence` annotation on the element behind a spec/internal finding) means this exact
427
+ question was already reasoned through and ratified. Classify it as Accepted divergence and
428
+ cite the annotation; don't propose a new red note or re-open the judgment call from scratch.