@milehimikey/em 1.13.1 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/em-critic.md +26 -0
- package/.claude/agents/em-implementer.md +34 -0
- package/.claude/agents/em-reviewer.md +31 -0
- package/.claude/agents/em-validator.md +27 -0
- package/.claude/skills/event-modeling/SKILL.md +50 -11
- package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
- package/.claude/skills/event-modeling-design/SKILL.md +127 -18
- package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
- package/.claude/skills/event-modeling-engagement/SKILL.md +192 -0
- package/.claude/skills/event-modeling-engagement/reference/engagement.md +143 -0
- package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
- package/.claude/skills/event-modeling-implement/reference/implement.md +38 -11
- package/.claude/skills/event-modeling-review/SKILL.md +2 -2
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +279 -189
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +61 -0
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +116 -5
- package/.claude/skills/event-modeling-shared/templates/constitution.md +18 -1
- package/.claude/skills/event-modeling-shared/templates/slice.md +64 -39
- package/.claude/skills/event-modeling-shared/templates/state.md +5 -1
- package/.claude-plugin/marketplace.json +14 -0
- package/README.md +25 -11
- package/dist/catalog/apiFirst.d.ts +28 -0
- package/dist/catalog/apiFirst.js +49 -0
- package/dist/catalog/apiFirst.js.map +1 -0
- package/dist/catalog/docJoin.d.ts +13 -0
- package/dist/catalog/docJoin.js +11 -0
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/docModelConsistencyValidate.js +44 -4
- package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
- package/dist/catalog/invariantsValidate.d.ts +4 -0
- package/dist/catalog/invariantsValidate.js +102 -0
- package/dist/catalog/invariantsValidate.js.map +1 -0
- package/dist/catalog/scenarios.d.ts +15 -0
- package/dist/catalog/scenarios.js +97 -0
- package/dist/catalog/scenarios.js.map +1 -0
- package/dist/catalog/sliceDoc.d.ts +8 -0
- package/dist/catalog/sliceDoc.js +9 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/catalog/sliceReadyValidate.d.ts +31 -4
- package/dist/catalog/sliceReadyValidate.js +66 -8
- package/dist/catalog/sliceReadyValidate.js.map +1 -1
- package/dist/catalog/sliceSections.d.ts +55 -0
- package/dist/catalog/sliceSections.js +317 -0
- package/dist/catalog/sliceSections.js.map +1 -0
- package/dist/cli/agentsMd.js +7 -1
- package/dist/cli/agentsMd.js.map +1 -1
- package/dist/cli/api.d.ts +90 -0
- package/dist/cli/api.js +318 -0
- package/dist/cli/api.js.map +1 -0
- package/dist/cli/ciInit.d.ts +82 -21
- package/dist/cli/ciInit.js +468 -64
- package/dist/cli/ciInit.js.map +1 -1
- package/dist/cli/conformScope.d.ts +28 -0
- package/dist/cli/conformScope.js +67 -0
- package/dist/cli/conformScope.js.map +1 -1
- package/dist/cli/coverage.d.ts +9 -0
- package/dist/cli/coverage.js +60 -8
- package/dist/cli/coverage.js.map +1 -1
- package/dist/cli/defer.d.ts +55 -0
- package/dist/cli/defer.js +244 -0
- package/dist/cli/defer.js.map +1 -0
- package/dist/cli/engagement.d.ts +210 -0
- package/dist/cli/engagement.js +444 -0
- package/dist/cli/engagement.js.map +1 -0
- package/dist/cli/engagementFile.d.ts +86 -0
- package/dist/cli/engagementFile.js +255 -0
- package/dist/cli/engagementFile.js.map +1 -0
- package/dist/cli/metrics.d.ts +10 -0
- package/dist/cli/metrics.js +49 -1
- package/dist/cli/metrics.js.map +1 -1
- package/dist/cli/pluginPin.d.ts +55 -0
- package/dist/cli/pluginPin.js +188 -0
- package/dist/cli/pluginPin.js.map +1 -0
- package/dist/cli/ratify.d.ts +27 -2
- package/dist/cli/ratify.js +112 -13
- package/dist/cli/ratify.js.map +1 -1
- package/dist/cli/reratify.d.ts +4 -2
- package/dist/cli/reratify.js +41 -16
- package/dist/cli/reratify.js.map +1 -1
- package/dist/cli/scopeInputs.d.ts +48 -0
- package/dist/cli/scopeInputs.js +176 -0
- package/dist/cli/scopeInputs.js.map +1 -0
- package/dist/cli/skillCheck.d.ts +15 -1
- package/dist/cli/skillCheck.js +22 -1
- package/dist/cli/skillCheck.js.map +1 -1
- package/dist/cli/skillDirs.d.ts +5 -0
- package/dist/cli/skillDirs.js +13 -1
- package/dist/cli/skillDirs.js.map +1 -1
- package/dist/cli/skillSync.d.ts +4 -0
- package/dist/cli/skillSync.js +23 -0
- package/dist/cli/skillSync.js.map +1 -1
- package/dist/cli/sliceNew.d.ts +11 -7
- package/dist/cli/sliceNew.js +91 -18
- package/dist/cli/sliceNew.js.map +1 -1
- package/dist/cli/sliceSync.d.ts +50 -0
- package/dist/cli/sliceSync.js +170 -0
- package/dist/cli/sliceSync.js.map +1 -0
- package/dist/cli/status.d.ts +24 -1
- package/dist/cli/status.js +20 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/systemInputs.d.ts +53 -10
- package/dist/cli/systemInputs.js +186 -11
- package/dist/cli/systemInputs.js.map +1 -1
- package/dist/cli/upgrade.d.ts +16 -3
- package/dist/cli/upgrade.js +366 -30
- package/dist/cli/upgrade.js.map +1 -1
- package/dist/cli/usageLog.d.ts +4 -2
- package/dist/cli/usageLog.js +4 -2
- package/dist/cli/usageLog.js.map +1 -1
- package/dist/cli.js +599 -94
- package/dist/cli.js.map +1 -1
- package/dist/emit/api.d.ts +29 -0
- package/dist/emit/api.js +220 -0
- package/dist/emit/api.js.map +1 -0
- package/dist/emit/apiCheckJson.d.ts +3 -0
- package/dist/emit/apiCheckJson.js +21 -0
- package/dist/emit/apiCheckJson.js.map +1 -0
- package/dist/emit/codeownersJson.d.ts +3 -0
- package/dist/emit/codeownersJson.js +19 -0
- package/dist/emit/codeownersJson.js.map +1 -0
- package/dist/emit/diffJson.d.ts +1 -1
- package/dist/emit/diffJson.js +8 -1
- package/dist/emit/diffJson.js.map +1 -1
- package/dist/emit/engagementJson.d.ts +8 -0
- package/dist/emit/engagementJson.js +46 -0
- package/dist/emit/engagementJson.js.map +1 -0
- package/dist/emit/glossaryJson.d.ts +1 -1
- package/dist/emit/glossaryJson.js +4 -1
- package/dist/emit/glossaryJson.js.map +1 -1
- package/dist/emit/json.d.ts +17 -1
- package/dist/emit/json.js +30 -1
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/metricsJson.d.ts +1 -1
- package/dist/emit/metricsJson.js +4 -1
- package/dist/emit/metricsJson.js.map +1 -1
- package/dist/emit/queryJson.d.ts +1 -1
- package/dist/emit/queryJson.js +7 -1
- package/dist/emit/queryJson.js.map +1 -1
- package/dist/emit/scopeJson.d.ts +12 -0
- package/dist/emit/scopeJson.js +26 -0
- package/dist/emit/scopeJson.js.map +1 -0
- package/dist/emit/skillCheckJson.d.ts +3 -2
- package/dist/emit/skillCheckJson.js +10 -2
- package/dist/emit/skillCheckJson.js.map +1 -1
- package/dist/emit/statusJson.d.ts +1 -1
- package/dist/emit/statusJson.js +15 -1
- package/dist/emit/statusJson.js.map +1 -1
- package/dist/emit/systemJson.d.ts +15 -8
- package/dist/emit/systemJson.js +28 -13
- package/dist/emit/systemJson.js.map +1 -1
- package/dist/emit/validateJson.d.ts +3 -3
- package/dist/emit/validateJson.js +10 -4
- package/dist/emit/validateJson.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +163 -31
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/diff.d.ts +4 -1
- package/dist/model/diff.js +44 -8
- package/dist/model/diff.js.map +1 -1
- package/dist/model/glossary.d.ts +2 -0
- package/dist/model/glossary.js +1 -0
- package/dist/model/glossary.js.map +1 -1
- package/dist/model/model.d.ts +55 -2
- package/dist/model/model.js +57 -0
- package/dist/model/model.js.map +1 -1
- package/dist/model/qualifiedRef.d.ts +18 -0
- package/dist/model/qualifiedRef.js +26 -0
- package/dist/model/qualifiedRef.js.map +1 -1
- package/dist/model/queryIndex.d.ts +9 -1
- package/dist/model/queryIndex.js +18 -2
- package/dist/model/queryIndex.js.map +1 -1
- package/dist/model/rules.d.ts +143 -2
- package/dist/model/rules.js +162 -3
- package/dist/model/rules.js.map +1 -1
- package/dist/model/validate.d.ts +40 -0
- package/dist/model/validate.js +147 -1
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +31 -3
- package/dist/parser/parser.js +148 -14
- package/dist/parser/parser.js.map +1 -1
- package/dist/query/format.js +1 -1
- package/dist/query/format.js.map +1 -1
- package/dist/query/verbs.d.ts +6 -0
- package/dist/query/verbs.js +6 -2
- package/dist/query/verbs.js.map +1 -1
- package/dist/system/adaptation.d.ts +14 -0
- package/dist/system/adaptation.js +49 -0
- package/dist/system/adaptation.js.map +1 -0
- package/dist/system/codeowners.d.ts +77 -0
- package/dist/system/codeowners.js +242 -0
- package/dist/system/codeowners.js.map +1 -0
- package/dist/system/manifest.d.ts +10 -5
- package/dist/system/manifest.js +54 -26
- package/dist/system/manifest.js.map +1 -1
- package/dist/system/migrateManifest.d.ts +28 -0
- package/dist/system/migrateManifest.js +155 -0
- package/dist/system/migrateManifest.js.map +1 -0
- package/dist/system/scope.d.ts +46 -0
- package/dist/system/scope.js +132 -0
- package/dist/system/scope.js.map +1 -0
- package/dist/system/verify.d.ts +50 -13
- package/dist/system/verify.js +179 -58
- package/dist/system/verify.js.map +1 -1
- package/dist/templates.d.ts +2 -2
- package/dist/templates.js +23 -2
- package/dist/templates.js.map +1 -1
- package/package.json +5 -2
- package/plugin/.claude-plugin/plugin.json +18 -0
- package/plugin/agents/em-critic.md +27 -0
- package/plugin/agents/em-implementer.md +35 -0
- package/plugin/agents/em-reviewer.md +32 -0
- package/plugin/agents/em-validator.md +28 -0
- package/plugin/shared/reference/em-dsl.md +871 -0
- package/plugin/shared/reference/methodology.md +329 -0
- package/plugin/shared/reference/operating-principles.md +222 -0
- package/plugin/shared/reference/slice-doc-schema.md +574 -0
- package/plugin/shared/templates/conformance-report.md +85 -0
- package/plugin/shared/templates/constitution.md +136 -0
- package/plugin/shared/templates/model-readme.md +46 -0
- package/plugin/shared/templates/slice.md +251 -0
- package/plugin/shared/templates/state.md +77 -0
- package/plugin/skills/conform/SKILL.md +93 -0
- package/plugin/skills/conform/reference/conform.md +428 -0
- package/plugin/skills/design/SKILL.md +298 -0
- package/plugin/skills/discover/SKILL.md +90 -0
- package/plugin/skills/discover/reference/extract.md +147 -0
- package/plugin/skills/engagement/SKILL.md +193 -0
- package/plugin/skills/engagement/reference/engagement.md +144 -0
- package/plugin/skills/event-modeling/SKILL.md +132 -0
- package/plugin/skills/implement/SKILL.md +61 -0
- package/plugin/skills/implement/reference/implement.md +439 -0
- package/plugin/skills/review/SKILL.md +74 -0
- package/dist/emit/typespec.d.ts +0 -26
- package/dist/emit/typespec.js +0 -356
- package/dist/emit/typespec.js.map +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: conform
|
|
3
|
+
em-version: 1.14.1
|
|
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.
|