@milehimikey/em 1.6.1 → 1.7.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 (92) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +60 -12
  2. package/.claude/skills/event-modeling/reference/conform.md +30 -8
  3. package/.claude/skills/event-modeling/reference/em-dsl.md +139 -23
  4. package/.claude/skills/event-modeling/reference/extract.md +2 -1
  5. package/.claude/skills/event-modeling/reference/implement.md +172 -0
  6. package/.claude/skills/event-modeling/templates/conformance-report.md +15 -9
  7. package/.claude/skills/event-modeling/templates/model-readme.md +3 -3
  8. package/.claude/skills/event-modeling/templates/slice.md +70 -4
  9. package/.claude/skills/event-modeling/templates/state.md +6 -5
  10. package/README.md +1 -0
  11. package/dist/catalog/build.d.ts +5 -0
  12. package/dist/catalog/build.js +1 -3
  13. package/dist/catalog/build.js.map +1 -1
  14. package/dist/catalog/docJoin.d.ts +39 -0
  15. package/dist/catalog/docJoin.js +86 -0
  16. package/dist/catalog/docJoin.js.map +1 -0
  17. package/dist/catalog/driftSignal.d.ts +16 -0
  18. package/dist/catalog/driftSignal.js +24 -0
  19. package/dist/catalog/driftSignal.js.map +1 -0
  20. package/dist/catalog/frontmatterCoherenceValidate.d.ts +8 -0
  21. package/dist/catalog/frontmatterCoherenceValidate.js +52 -0
  22. package/dist/catalog/frontmatterCoherenceValidate.js.map +1 -0
  23. package/dist/catalog/lineageValidate.d.ts +9 -0
  24. package/dist/catalog/lineageValidate.js +161 -0
  25. package/dist/catalog/lineageValidate.js.map +1 -0
  26. package/dist/catalog/readSliceDoc.d.ts +2 -0
  27. package/dist/catalog/readSliceDoc.js +21 -0
  28. package/dist/catalog/readSliceDoc.js.map +1 -0
  29. package/dist/catalog/sliceDoc.d.ts +94 -3
  30. package/dist/catalog/sliceDoc.js +157 -7
  31. package/dist/catalog/sliceDoc.js.map +1 -1
  32. package/dist/catalog/sliceReadyValidate.d.ts +11 -0
  33. package/dist/catalog/sliceReadyValidate.js +85 -0
  34. package/dist/catalog/sliceReadyValidate.js.map +1 -0
  35. package/dist/cli/diff-inputs.d.ts +20 -0
  36. package/dist/cli/diff-inputs.js +75 -18
  37. package/dist/cli/diff-inputs.js.map +1 -1
  38. package/dist/cli/ledgerCheck.d.ts +38 -0
  39. package/dist/cli/ledgerCheck.js +116 -0
  40. package/dist/cli/ledgerCheck.js.map +1 -0
  41. package/dist/cli/skillCheck.d.ts +18 -0
  42. package/dist/cli/skillCheck.js +63 -0
  43. package/dist/cli/skillCheck.js.map +1 -0
  44. package/dist/cli/skillSync.d.ts +22 -0
  45. package/dist/cli/skillSync.js +69 -0
  46. package/dist/cli/skillSync.js.map +1 -0
  47. package/dist/cli/skillVersionCheck.d.ts +32 -0
  48. package/dist/cli/skillVersionCheck.js +145 -0
  49. package/dist/cli/skillVersionCheck.js.map +1 -0
  50. package/dist/cli.d.ts +3 -1
  51. package/dist/cli.js +199 -32
  52. package/dist/cli.js.map +1 -1
  53. package/dist/emit/diffJson.d.ts +1 -1
  54. package/dist/emit/diffJson.js +18 -14
  55. package/dist/emit/diffJson.js.map +1 -1
  56. package/dist/emit/json.d.ts +10 -25
  57. package/dist/emit/json.js +70 -103
  58. package/dist/emit/json.js.map +1 -1
  59. package/dist/emit/ledgerJson.d.ts +5 -0
  60. package/dist/emit/ledgerJson.js +25 -0
  61. package/dist/emit/ledgerJson.js.map +1 -0
  62. package/dist/emit/skillCheckJson.d.ts +5 -0
  63. package/dist/emit/skillCheckJson.js +32 -0
  64. package/dist/emit/skillCheckJson.js.map +1 -0
  65. package/dist/model/diff.d.ts +44 -1
  66. package/dist/model/diff.js +51 -9
  67. package/dist/model/diff.js.map +1 -1
  68. package/dist/model/model.d.ts +1 -1
  69. package/dist/model/refs.d.ts +23 -0
  70. package/dist/model/refs.js +75 -0
  71. package/dist/model/refs.js.map +1 -0
  72. package/dist/model/rules.d.ts +252 -0
  73. package/dist/model/rules.js +253 -0
  74. package/dist/model/rules.js.map +1 -0
  75. package/dist/model/validate.d.ts +23 -1
  76. package/dist/model/validate.js +168 -47
  77. package/dist/model/validate.js.map +1 -1
  78. package/dist/parser/ast.d.ts +3 -2
  79. package/dist/parser/parser.js +23 -11
  80. package/dist/parser/parser.js.map +1 -1
  81. package/dist/pipeline.d.ts +9 -0
  82. package/dist/pipeline.js +4 -2
  83. package/dist/pipeline.js.map +1 -1
  84. package/dist/render/sliceStatus.js +1 -1
  85. package/dist/render/sliceStatus.js.map +1 -1
  86. package/dist/util/isMainModule.d.ts +14 -0
  87. package/dist/util/isMainModule.js +33 -0
  88. package/dist/util/isMainModule.js.map +1 -0
  89. package/dist/util/walkDir.d.ts +4 -0
  90. package/dist/util/walkDir.js +28 -0
  91. package/dist/util/walkDir.js.map +1 -0
  92. package/package.json +5 -2
@@ -16,11 +16,15 @@ Replace bracketed placeholders; delete guidance comments before finishing.
16
16
 
17
17
  ## Summary
18
18
 
19
- | Surface | Real drift | Model gap | Internal inconsistency | Accepted divergence | Uncertainty |
20
- |---|---|---|---|---|---|
21
- | Structural | {{n}} | {{n}} | — | {{n}} | {{n}} |
22
- | Spec | {{n}} | {{n}} | — | {{n}} | {{n}} |
23
- | Internal | — | — | {{n}} | {{n}} | {{n}} |
19
+ | Surface | Real drift | Model gap | Internal inconsistency | Accepted divergence | Unpropagated delta | Uncertainty |
20
+ |---|---|---|---|---|---|---|
21
+ | Structural | {{n}} | {{n}} | — | {{n}} | {{n}} | {{n}} |
22
+ | Spec | {{n}} | {{n}} | — | {{n}} | {{n}} | {{n}} |
23
+ | Internal | — | — | {{n}} | {{n}} | — | {{n}} |
24
+
25
+ **Unpropagated delta** — a slice re-ratified past its shipped version (`slice.doc.driftSignal:
26
+ "unpropagated-delta"` in `em export --json`): a known state, not a code defect. See
27
+ `reference/conform.md` step 4.
24
28
 
25
29
  {{One or two sentences: overall read — clean, mostly clean with N items, or notably drifted.}}
26
30
 
@@ -32,15 +36,17 @@ Replace bracketed placeholders; delete guidance comments before finishing.
32
36
  ### {{n}}. {{Short finding title}}
33
37
 
34
38
  - **Surface:** {{structural | spec | internal}}
35
- - **Classification:** {{real drift | model gap | internal inconsistency | accepted divergence}}
39
+ - **Classification:** {{real drift | model gap | internal inconsistency | accepted divergence | unpropagated delta}}
36
40
  - **Slice:** {{slice name}}
37
41
  - **Evidence:** {{file path(s) and line/symbol, or the `em diff --json` entry (type + name) —
38
- for accepted divergence, the `divergence "..."` annotation text itself}}
42
+ for accepted divergence, the `divergence "..."` annotation text itself; for unpropagated
43
+ delta, `slice.doc.driftSignal` and `slice.doc.version` from `em export --json`}}
39
44
  - **Claim:** {{what the model/doc says vs. what the code/doc shows — the actual disagreement}}
40
45
  - **Proposed red note:** `issue "conformance: {{text}}"` on {{element}} (slice "{{slice}}")
41
46
  <!-- Omit this line entirely for internal-inconsistency findings with no clear side to flag,
42
- for any accepted-divergence finding (already ratified — don't propose re-ratifying it),
43
- or when the finding doesn't warrant a red note (say why instead). -->
47
+ for any accepted-divergence or unpropagated-delta finding (already ratified, or already
48
+ a known pending-ship state — don't propose re-ratifying either), or when the finding
49
+ doesn't warrant a red note (say why instead). -->
44
50
 
45
51
  ## Scenario / invariant coverage
46
52
 
@@ -34,9 +34,9 @@ never connected to one another.
34
34
  <!-- The canonical slice index — the ONE place slices are enumerated (the state file
35
35
  points here rather than keeping its own copy). Keep it in sync as slice docs are
36
36
  written and as statuses move. -->
37
- | # | Slice | Pattern | Status | Design doc |
38
- |---|-------|---------|--------|------------|
39
- | 1 | {{Slice Name}} | {{Pattern}} | {{none / draft / reviewed / ready-to-implement / implemented}} | [slices/{{slice-name}}.md](slices/{{slice-name}}.md) |
37
+ | # | Slice | Pattern | Status | Implemented in | Design doc |
38
+ |---|-------|---------|--------|----------------|------------|
39
+ | 1 | {{Slice Name}} | {{Pattern}} | {{no doc yet / draft / reviewed / ready-to-implement / implemented}} | {{PR/commit link once implemented, else —}} | [slices/{{slice-name}}.md](slices/{{slice-name}}.md) |
40
40
 
41
41
  ## Status
42
42
  See [`.event-modeling.md`](.event-modeling.md) for current phase, decisions, and open questions.
@@ -4,18 +4,84 @@ linked from the .em model with: note "slices/<slice-name>.md" on the slice's d
4
4
  Fill every section through Socratic questioning. Leave "Open Questions" rather than guessing.
5
5
  Replace the bracketed placeholders; delete guidance comments before finishing.
6
6
 
7
+ The frontmatter below is the canonical, machine-read metadata dialect — `em`'s own parser
8
+ (src/catalog/sliceDoc.ts) reads `status:`, `version:`, `pattern:`, and the lineage keys
9
+ (`split-from:`/`merged-from:`/`superseded-by:`) from it. `pattern` is kebab-case
10
+ (state-change/state-view/automation/translation) even though the skill's prose always says
11
+ "State Change"/"State View"/etc. — the frontmatter value is a machine key, not a display label.
12
+ Older docs using a `- **Status:** ...` bullet line instead of frontmatter still parse (legacy/
13
+ accepted input), but new docs should always use this frontmatter form; `version` and lineage
14
+ have no legacy form — frontmatter-only from day one.
15
+
16
+ `version` is this slice's own ratified-content version — starts at `1`, bumps when a delta is
17
+ ratified — distinct from `schemaVersion`, which versions the frontmatter dialect itself, not
18
+ this slice. When a ratified change lands on an already-`implemented` slice: bump `version`,
19
+ flip `status` back to `ready-to-implement` (it tracks the CURRENT version's implementation
20
+ state — `implementedIn` legitimately keeps naming the PRIOR version's PR until the new version
21
+ ships; that mismatch is an intended drift signal, not a bug), and fill in the `## Delta` section
22
+ below with this hop's change, replacing whatever it held before (it always shows only the latest
23
+ hop — full history lives in git, same-commit-ratification convention). Full grammar — the fixed
24
+ heading, the four operation subsections, why the heading never varies, the replace-not-accumulate
25
+ lifecycle — is documented in docs/slice-doc-schema.md#delta-section-grammar-and-lifecycle; treat
26
+ it the same Socratic way as every other section here, don't generate it mechanically.
27
+
28
+ The three lineage keys only apply when this doc was produced by a split, merge, or rename —
29
+ delete them otherwise; most slices never carry them. Grammar: `<slice-key>@v<N>`, where
30
+ `<slice-key>` is the referenced slice's kebab-case filename stem.
31
+
32
+ Full machine schema — required-vs-optional keys per `status`, value types/enums, the
33
+ unknown-key policy — is documented in docs/slice-doc-schema.md.
34
+
7
35
  The diagram below is generated, not hand-drawn: `em render <model>.em --slice "{{Slice Name}}"
8
36
  -o slices/{{slice-name}}.svg` (kebab-case the slice name to match this doc's own filename).
9
37
  -->
10
38
 
39
+ ---
40
+ schemaVersion: 1
41
+ pattern: {{state-change | state-view | automation | translation}}
42
+ swimlane: {{Persona/Actor}} → {{Context/Aggregate}}
43
+ status: {{draft | reviewed | ready-to-implement | implemented}}
44
+ version: 1
45
+ implementedIn: {{PR/commit link — fill in once status is `implemented`}}
46
+ # Lineage — only when this doc exists because of a split, merge, or rename (delete these three
47
+ # lines otherwise). Grammar: <slice-key>@v<N>. See
48
+ # docs/slice-doc-schema.md#lineage-grammar-and-cardinality.
49
+ # split-from: <slice-key>@v<N>
50
+ # merged-from: <slice-key>@v<N>, <slice-key>@v<N>
51
+ # superseded-by: <slice-key>@v<N>, <slice-key>@v<N>
52
+ ---
11
53
  # Slice: {{Slice Name}}
12
54
 
13
55
  ![Diagram](./{{slice-name}}.svg)
14
56
 
15
- - **Pattern:** {{State Change | State View | Automation | Translation}}
16
- - **Swimlane:** {{Persona/Actor}} → {{Context/Aggregate}}
17
- - **Status:** {{draft | reviewed | ready-to-implement | implemented}}
18
- - **Implemented in:** {{PR/commit link — fill in once status is `implemented`}}
57
+ <!-- Only on re-ratification of an already-`implemented` slice — omit this whole section on a
58
+ slice's first version (v1 has no delta yet). The heading is always the literal `## Delta`,
59
+ never `## Delta: vX → vY` — hop metadata is a display line inside the section instead (see
60
+ docs/slice-doc-schema.md#delta-section-grammar-and-lifecycle for why). Replace this
61
+ section's content wholesale on the next re-ratification — it shows only the latest hop, not
62
+ an accumulating log. Omit any of the four subsections below with no entries this hop; keep
63
+ the remaining ones in this order (Added/Modified/Removed/Renamed). `Renamed` is for a
64
+ requirement/invariant renamed within this slice, not a substitute for the frontmatter
65
+ `split-from`/`merged-from`/`superseded-by` lineage keys above, which record the slice-doc
66
+ itself splitting, merging, or being renamed. -->
67
+ ## Delta
68
+ **v{{X}} → v{{Y}}, ratified {{date}}** — {{one-line summary of the ratified change}}
69
+
70
+ ### Added
71
+ #### Requirement: {{title}} ({{stable ID, e.g. an INV-n from Invariants below}})
72
+ {{requirement text — same voice as an Invariants entry}}
73
+ ##### Scenario: {{name}}
74
+ - **GIVEN** {{...}} **WHEN** {{...}} **THEN** {{...}}
75
+
76
+ ### Modified
77
+ <!-- Same shape as Added — the stable ID names the requirement that changed. -->
78
+
79
+ ### Removed
80
+ #### Requirement: {{title}} ({{stable ID}})
81
+ {{why it was removed}}
82
+
83
+ ### Renamed
84
+ - {{old title}} ({{old ID}}) → {{new title}} ({{new ID}})
19
85
 
20
86
  ## Intent
21
87
  {{Why this slice exists — the user or business goal it serves, in one or two sentences. Note the
@@ -7,7 +7,7 @@ Keep it current at the end of every working session.
7
7
  # Event Modeling Progress — {{Model Name}}
8
8
 
9
9
  - **Model file:** `{{model-name}}.em`
10
- - **Current phase:** {{extract | discover | model | slice}}
10
+ - **Current phase:** {{discover | extract | model | slice | implement | conform | review | validate}}
11
11
  - **Current step:** {{1–7, see methodology; or extraction round R1–R7}}
12
12
  - **Last updated:** {{YYYY-MM-DD}}
13
13
  - **Last conformance:** {{YYYY-MM-DD @ <target-repo revision> — report: conformance/<date>-report.md | never}}
@@ -50,10 +50,11 @@ Keep it current at the end of every working session.
50
50
 
51
51
  ## Usage log
52
52
  <!-- The team's only usage signal today (see docs/usage-data.md) — cheap and coarse on purpose.
53
- One line per session: phase(s) touched, and validate diagnostic *categories* hit (the rule
54
- name from docs/validation.md's tables, e.g. "read model nothing consumes" — never the full
55
- instance message or any domain content). Append, never edit past entries. -->
56
- - {{YYYY-MM-DD}}: phases: {{discover | extract | model | slice | conform | review | validate | watch, ...}} — validate: {{diagnostic category (docs/usage-data.md#categories), ... | none}}
53
+ One line per session: phase(s) touched, and validate diagnostic *categories* hit (one of
54
+ the exact fixed strings in docs/usage-data.md#categories, e.g. "read model has no
55
+ consumer" — never the full instance message or any domain content). Append, never edit
56
+ past entries. -->
57
+ - {{YYYY-MM-DD}}: phases: {{discover | extract | model | slice | implement | conform | review | validate | watch, ...}} — validate: {{diagnostic category (docs/usage-data.md#categories), ... | none}}
57
58
 
58
59
  ## Open questions / parking lot
59
60
  <!-- Unresolved items to bring back to the user. Never guess these. Metadata is optional and
package/README.md CHANGED
@@ -84,6 +84,7 @@ built this way.
84
84
  |---|---|
85
85
  | [docs/tutorial.md](docs/tutorial.md) | Learn the tool by building a model from scratch |
86
86
  | [docs/workflow.md](docs/workflow.md) | The model lifecycle: specify, gate, hand off, track change, detect drift |
87
+ | [docs/process.md](docs/process.md) | Who does what: where humans are required, where agents work with review |
87
88
  | [docs/patterns.md](docs/patterns.md) | The four Event Modeling patterns and their DSL shapes |
88
89
  | [docs/dsl.md](docs/dsl.md) | Full DSL reference: keywords, `from`, `again`, fields, notes |
89
90
  | [docs/cli.md](docs/cli.md) | Every command and flag |
@@ -1,5 +1,6 @@
1
1
  import { NormalizedModel } from "../model/model.js";
2
2
  import { Grid } from "../layout/grid.js";
3
+ import { RefsResult } from "../model/refs.js";
3
4
  import { Diagnostic } from "../model/validate.js";
4
5
  export interface CatalogModelInput {
5
6
  /** The .em file as given on the command line — used for the doc-discovery path and display. */
@@ -7,6 +8,10 @@ export interface CatalogModelInput {
7
8
  model: NormalizedModel;
8
9
  grid: Grid;
9
10
  dot: string;
11
+ /** Already-computed by the same `compile()` call that produced `model`/`grid` (MIL-91) —
12
+ * never recomputed here, so its ref-collision diagnostics (already in `compile()`'s own
13
+ * `diagnostics`) aren't emitted a second time. */
14
+ refs: RefsResult;
10
15
  }
11
16
  export interface CatalogOptions {
12
17
  outDir: string;
@@ -15,7 +15,6 @@ import { copyFile, mkdir, writeFile } from "node:fs/promises";
15
15
  import { basename, dirname, extname, join } from "node:path";
16
16
  import { layoutDot, composeSvg, writeRendered } from "../render/render.js";
17
17
  import { buildSliceDiagram } from "../render/sliceDiagram.js";
18
- import { computeRefs } from "../emit/json.js";
19
18
  import { dedupe, kebabSlug } from "../util/slug.js";
20
19
  import { classifySlicePattern } from "./classify.js";
21
20
  import { parseSliceDoc } from "./sliceDoc.js";
@@ -29,7 +28,7 @@ export async function buildCatalog(inputs, opts) {
29
28
  const modelSummaries = [];
30
29
  const diagnostics = [];
31
30
  let sliceCount = 0;
32
- for (const { file, model, grid, dot } of inputs) {
31
+ for (const { file, model, grid, dot, refs } of inputs) {
33
32
  const modelKey = dedupe(kebabSlug(basename(file, extname(file))), usedModelKeys, "~");
34
33
  const modelDir = join(outDir, modelKey);
35
34
  const slicesDir = join(modelDir, "slices");
@@ -40,7 +39,6 @@ export async function buildCatalog(inputs, opts) {
40
39
  const raw = await layoutDot(dot);
41
40
  const mainSvg = composeSvg(raw, model, grid, dirname(file), modelDir);
42
41
  await writeRendered(mainSvg, join(modelDir, diagramFile), format);
43
- const refs = computeRefs(model);
44
42
  if (refs.diagnostics.length > 0)
45
43
  diagnostics.push({ file, diagnostics: refs.diagnostics });
46
44
  const sliceSummaries = [];
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/catalog/build.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/catalog/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { Slice } from "../model/model.js";
2
+ import { Diagnostic } from "../model/validate.js";
3
+ import { DriftSignalKind } from "./driftSignal.js";
4
+ import { SliceRef } from "./sliceDoc.js";
5
+ export type DocReason = "no-doc-bound" | "binding-missing-file" | "frontmatter-invalid" | null;
6
+ /** A slice's export-facing doc join — always a non-null object (never JSON `null`), matching
7
+ * every other optional field in `em export`'s style: explicit-null-on-a-stable-key, never a
8
+ * nullable wrapper. `reason` is null exactly when `found` is true and the frontmatter parsed
9
+ * cleanly; otherwise it names which of the three broken states applies. */
10
+ export interface SliceDocExport {
11
+ found: boolean;
12
+ /** The conventional path (`slices/<sliceKey>.md`), always present regardless of `found` —
13
+ * the path a doc would need to exist at, and (when `found`) the path it was read from. */
14
+ path: string;
15
+ reason: DocReason;
16
+ status: string | null;
17
+ version: number | null;
18
+ implementedIn: string | null;
19
+ splitFrom: SliceRef | null;
20
+ mergedFrom: SliceRef[];
21
+ supersededBy: SliceRef[];
22
+ /** Implementation-drift classification (MIL-85), from `status`+`implementedIn` alone — see
23
+ * catalog/driftSignal.ts. Null only when `found` is false (nothing to classify); paired with
24
+ * `version` above from the same doc parse — a finding built from `driftSignal` should always
25
+ * cite `version` alongside it, never cache one without the other. */
26
+ driftSignal: DriftSignalKind | null;
27
+ }
28
+ export interface SliceDocJoinResult {
29
+ doc: SliceDocExport;
30
+ diagnostics: Diagnostic[];
31
+ }
32
+ /**
33
+ * Resolve one slice's doc join. `baseDir` is the `.em` file's directory (doc paths, like note
34
+ * paths everywhere else in em, are relative to it). `sliceRef`/`elementRefOf` supply the
35
+ * export-stable refs (from the same `computeRefs()` result `em export`'s per-element/slice
36
+ * entries use) so the returned diagnostics point at the same identities every other export
37
+ * diagnostic does — never a second identifier scheme.
38
+ */
39
+ export declare function resolveSliceDocJoin(slice: Slice, sliceKey: string, baseDir: string, elementRefOf: (id: string) => string): SliceDocJoinResult;
@@ -0,0 +1,86 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // The `em export` slice-doc join (MIL-91): for each slice, resolve whether a design doc was
3
+ // declared, found, and well-formed, and if so pull its canonical frontmatter fields into the
4
+ // export JSON. Used ONLY by `em export` (src/emit/json.ts) — `em catalog`'s own doc lookup
5
+ // (catalog/build.ts) deliberately keeps its simpler existsSync+parseSliceDoc convention: it
6
+ // has a standing tested invariant that catalog's doc discovery ignores `note` entirely
7
+ // (test/catalog.e2e.test.ts), and doesn't need this module's 3-state reason granularity. The
8
+ // underlying read (`readSliceDoc.ts`) is shared with `em diff`'s lineage annotation
9
+ // (model/diff.ts) and `em validate`'s lineage-ref resolution (catalog/lineageValidate.ts,
10
+ // MIL-84) — this module's `note`-binding gate and 3-state `DocReason` stay export-specific.
11
+ //
12
+ // Three states (MIL-91): the `note "slices/<name>.md"` binding on an element is the
13
+ // load-bearing fact — binding a note IS the ratified declaration that a doc should exist. No
14
+ // note binding is a normal, unremarkable state (a slice that hasn't reached specify yet); a
15
+ // binding pointing at a missing file, or a doc whose frontmatter is missing or malformed, are
16
+ // both broken-contract states and warn.
17
+ import { makeDiag } from "../model/rules.js";
18
+ import { classifyImplementationDrift } from "./driftSignal.js";
19
+ import { readSliceDoc } from "./readSliceDoc.js";
20
+ import { hasUsableFrontmatter } from "./sliceDoc.js";
21
+ const EMPTY_CONTENT = {
22
+ status: null,
23
+ version: null,
24
+ implementedIn: null,
25
+ splitFrom: null,
26
+ mergedFrom: [],
27
+ supersededBy: [],
28
+ driftSignal: null,
29
+ };
30
+ /**
31
+ * Resolve one slice's doc join. `baseDir` is the `.em` file's directory (doc paths, like note
32
+ * paths everywhere else in em, are relative to it). `sliceRef`/`elementRefOf` supply the
33
+ * export-stable refs (from the same `computeRefs()` result `em export`'s per-element/slice
34
+ * entries use) so the returned diagnostics point at the same identities every other export
35
+ * diagnostic does — never a second identifier scheme.
36
+ */
37
+ export function resolveSliceDocJoin(slice, sliceKey, baseDir, elementRefOf) {
38
+ const path = `slices/${sliceKey}.md`;
39
+ const boundEls = slice.elements.filter((el) => el.note === path);
40
+ if (boundEls.length === 0) {
41
+ return { doc: { found: false, path, reason: "no-doc-bound", ...EMPTY_CONTENT }, diagnostics: [] };
42
+ }
43
+ const parsed = readSliceDoc(baseDir, sliceKey);
44
+ if (!parsed) {
45
+ return {
46
+ doc: { found: false, path, reason: "binding-missing-file", ...EMPTY_CONTENT },
47
+ diagnostics: [
48
+ makeDiag("binding-missing-file", {
49
+ message: `slice "${slice.name}" notes "${path}" but no such file exists`,
50
+ line: boundEls[0].line,
51
+ refs: [sliceKey, ...boundEls.map((el) => elementRefOf(el.id))],
52
+ }),
53
+ ],
54
+ };
55
+ }
56
+ if (!hasUsableFrontmatter(parsed)) {
57
+ return {
58
+ doc: { found: true, path, reason: "frontmatter-invalid", ...EMPTY_CONTENT },
59
+ diagnostics: [
60
+ makeDiag("frontmatter-invalid", {
61
+ message: parsed.frontmatterPresent
62
+ ? `slice doc "${path}" is missing required frontmatter keys: ${parsed.missingRequiredFields.join(", ")}`
63
+ : `slice doc "${path}" has no frontmatter block`,
64
+ line: slice.line,
65
+ refs: [sliceKey],
66
+ }),
67
+ ],
68
+ };
69
+ }
70
+ return {
71
+ doc: {
72
+ found: true,
73
+ path,
74
+ reason: null,
75
+ status: parsed.status,
76
+ version: parsed.version,
77
+ implementedIn: parsed.implementedIn,
78
+ splitFrom: parsed.splitFrom,
79
+ mergedFrom: parsed.mergedFrom,
80
+ supersededBy: parsed.supersededBy,
81
+ driftSignal: classifyImplementationDrift(parsed),
82
+ },
83
+ diagnostics: [],
84
+ };
85
+ }
86
+ //# sourceMappingURL=docJoin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docJoin.js","sourceRoot":"","sources":["../../src/catalog/docJoin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { SliceDoc } from "./sliceDoc.js";
2
+ export type DriftSignalKind =
3
+ /** status: implemented, implementedIn set — normal shipped state. */
4
+ "in-sync"
5
+ /** status not implemented, implementedIn absent — normal pre-ship state. */
6
+ | "never-implemented"
7
+ /** status not implemented, implementedIn still set — a ratified delta hasn't shipped yet.
8
+ * Expected, not a defect: never surface this as fresh drift or a validate diagnostic. */
9
+ | "unpropagated-delta"
10
+ /** status: implemented, implementedIn absent — genuine incoherence (em validate warns). */
11
+ | "implemented-without-link";
12
+ /**
13
+ * Classify a doc's status/implementedIn pair. Takes a `Pick` rather than the full `SliceDoc` so
14
+ * callers building a partial/synthetic doc (tests, future callers) don't need every field.
15
+ */
16
+ export declare function classifyImplementationDrift(doc: Pick<SliceDoc, "status" | "implementedIn">): DriftSignalKind;
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Classifies a slice doc's implementation-drift state from `status` + `implementedIn` alone
3
+ // (MIL-85). Pure — same spirit as sliceDoc.ts itself: no fs, no model, no baseDir — so
4
+ // `em export`'s doc join (catalog/docJoin.ts), `em validate`'s frontmatter-coherence check
5
+ // (catalog/frontmatterCoherenceValidate.ts), and the conform skill (reading `em export --json`'s
6
+ // `slice.doc.driftSignal`) all consume the exact same answer instead of re-deriving this
7
+ // predicate three different ways.
8
+ //
9
+ // The load-bearing distinction (docs/slice-doc-schema.md, "`status` under re-ratification"):
10
+ // re-ratifying a shipped slice flips `status` back off `implemented` while `implementedIn` keeps
11
+ // naming the prior version's PR. That combination — `unpropagated-delta` — is the EXPECTED drift
12
+ // signal, not staleness, and must never be flagged as incoherence. Only `implemented-without-link`
13
+ // (status: implemented, no link at all) is a genuine coherence problem worth a diagnostic.
14
+ /**
15
+ * Classify a doc's status/implementedIn pair. Takes a `Pick` rather than the full `SliceDoc` so
16
+ * callers building a partial/synthetic doc (tests, future callers) don't need every field.
17
+ */
18
+ export function classifyImplementationDrift(doc) {
19
+ const hasLink = typeof doc.implementedIn === "string" && doc.implementedIn.trim().length > 0;
20
+ if (doc.status === "implemented")
21
+ return hasLink ? "in-sync" : "implemented-without-link";
22
+ return hasLink ? "unpropagated-delta" : "never-implemented";
23
+ }
24
+ //# sourceMappingURL=driftSignal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driftSignal.js","sourceRoot":"","sources":["../../src/catalog/driftSignal.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,4FAA4F;AAC5F,uFAAuF;AACvF,2FAA2F;AAC3F,iGAAiG;AACjG,yFAAyF;AACzF,kCAAkC;AAClC,EAAE;AACF,6FAA6F;AAC7F,iGAAiG;AACjG,iGAAiG;AACjG,mGAAmG;AACnG,2FAA2F;AAe3F;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,GAA+C;IACzF,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7F,IAAI,GAAG,CAAC,MAAM,KAAK,aAAa;QAAE,OAAO,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,0BAA0B,CAAC;IAC1F,OAAO,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,mBAAmB,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { NormalizedModel } from "../model/model.js";
2
+ import { RefsResult } from "../model/refs.js";
3
+ import { Diagnostic } from "../model/validate.js";
4
+ /**
5
+ * Resolve every slice's doc and flag genuine status/implementedIn incoherence. `baseDir` is the
6
+ * `.em` file's directory (doc paths, like every other doc/note path in em, are relative to it).
7
+ */
8
+ export declare function validateFrontmatterCoherence(model: NormalizedModel, refs: RefsResult, baseDir: string): Diagnostic[];
@@ -0,0 +1,52 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // `em validate`'s frontmatter-coherence check (MIL-85): warns when a slice doc's `status` and
3
+ // `implementedIn` are genuinely incoherent — `status: implemented` with no `implementedIn` link
4
+ // at all. Deliberately narrow: re-ratifying a shipped slice flips `status` back off
5
+ // `implemented` while `implementedIn` keeps naming the prior version's PR (docs/slice-doc-
6
+ // schema.md, "`status` under re-ratification") — that combination is the EXPECTED drift signal
7
+ // of an unpropagated delta, not incoherence, and must stay silent here. See
8
+ // catalog/driftSignal.ts for the shared classification this check (and `em export`'s
9
+ // `slice.doc.driftSignal`) both read.
10
+ //
11
+ // Same fs-aware shape as catalog/lineageValidate.ts (MIL-84): a sibling module to
12
+ // model/validate.ts, not folded into it, because resolving a slice's doc needs `baseDir`/fs
13
+ // access that the rest of validate deliberately never touches.
14
+ //
15
+ // Requires usable frontmatter (`hasUsableFrontmatter()`, sliceDoc.ts) — the exact same gate
16
+ // `em export`'s docJoin.ts uses for `frontmatter-invalid`, shared as one function precisely so
17
+ // the two can't silently disagree on what counts as a readable doc. `implementedIn` is a
18
+ // frontmatter-only key (MIL-90/91) with no legacy body-label form (MIL-86's accepted-input
19
+ // dialect: `- **Status:** ...`, no frontmatter block), and every canonical field this check
20
+ // reads is itself frontmatter-only — so a doc with no fence, or one missing any required key,
21
+ // has nothing reliable enough to classify. Flagging it as incoherent would be a false positive
22
+ // against a doc that's merely unreadable, not one that's actually incoherent; stay silent
23
+ // rather than guess, matching export's classification of the same doc.
24
+ import { pushDiag } from "../model/rules.js";
25
+ import { classifyImplementationDrift } from "./driftSignal.js";
26
+ import { readSliceDoc } from "./readSliceDoc.js";
27
+ import { hasUsableFrontmatter } from "./sliceDoc.js";
28
+ /**
29
+ * Resolve every slice's doc and flag genuine status/implementedIn incoherence. `baseDir` is the
30
+ * `.em` file's directory (doc paths, like every other doc/note path in em, are relative to it).
31
+ */
32
+ export function validateFrontmatterCoherence(model, refs, baseDir) {
33
+ const diags = [];
34
+ model.slices.forEach((slice, i) => {
35
+ const sliceKey = refs.sliceKeys[i];
36
+ const doc = readSliceDoc(baseDir, sliceKey);
37
+ if (!doc || !hasUsableFrontmatter(doc))
38
+ return;
39
+ const drift = classifyImplementationDrift(doc);
40
+ if (drift !== "implemented-without-link")
41
+ return;
42
+ // "unpropagated-delta" falls through here silently — that's the point: a re-ratified slice
43
+ // whose implementedIn still names prior work is expected, not a defect.
44
+ pushDiag(diags, "frontmatter-coherence-implemented-without-link", {
45
+ message: `slice "${sliceKey}" has status: implemented but no implementedIn link (v${doc.version ?? "?"})`,
46
+ line: slice.line,
47
+ refs: [sliceKey],
48
+ });
49
+ });
50
+ return diags;
51
+ }
52
+ //# sourceMappingURL=frontmatterCoherenceValidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatterCoherenceValidate.js","sourceRoot":"","sources":["../../src/catalog/frontmatterCoherenceValidate.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,8FAA8F;AAC9F,gGAAgG;AAChG,oFAAoF;AACpF,2FAA2F;AAC3F,+FAA+F;AAC/F,4EAA4E;AAC5E,qFAAqF;AACrF,sCAAsC;AACtC,EAAE;AACF,kFAAkF;AAClF,4FAA4F;AAC5F,+DAA+D;AAC/D,EAAE;AACF,4FAA4F;AAC5F,+FAA+F;AAC/F,yFAAyF;AACzF,2FAA2F;AAC3F,4FAA4F;AAC5F,8FAA8F;AAC9F,+FAA+F;AAC/F,0FAA0F;AAC1F,uEAAuE;AAKvE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAAsB,EAAE,IAAgB,EAAE,OAAe;IACpG,MAAM,KAAK,GAAiB,EAAE,CAAC;IAE/B,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC;YAAE,OAAO;QAE/C,MAAM,KAAK,GAAG,2BAA2B,CAAC,GAAG,CAAC,CAAC;QAC/C,IAAI,KAAK,KAAK,0BAA0B;YAAE,OAAO;QACjD,2FAA2F;QAC3F,wEAAwE;QAExE,QAAQ,CAAC,KAAK,EAAE,gDAAgD,EAAE;YAChE,OAAO,EAAE,UAAU,QAAQ,yDAAyD,GAAG,CAAC,OAAO,IAAI,GAAG,GAAG;YACzG,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,CAAC,QAAQ,CAAC;SACjB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { NormalizedModel } from "../model/model.js";
2
+ import { RefsResult } from "../model/refs.js";
3
+ import { Diagnostic } from "../model/validate.js";
4
+ /**
5
+ * Resolve every slice's lineage refs against the current tree and report what the current tree
6
+ * can prove wrong. `baseDir` is the `.em` file's directory (doc paths, like every other
7
+ * doc/note path in em, are relative to it).
8
+ */
9
+ export declare function validateLineage(model: NormalizedModel, refs: RefsResult, baseDir: string): Diagnostic[];