@jasonbelmonti/markdown-engine 1.0.0 → 2.0.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 (308) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +75 -26
  3. package/SECURITY.md +5 -3
  4. package/dist/api/annotation-source-range.d.ts +7 -0
  5. package/dist/api/annotation-source-range.d.ts.map +1 -0
  6. package/dist/api/annotation-source-range.js +103 -0
  7. package/dist/api/annotation-source-range.js.map +1 -0
  8. package/dist/api/annotation-target-candidate.d.ts +10 -0
  9. package/dist/api/annotation-target-candidate.d.ts.map +1 -0
  10. package/dist/api/annotation-target-candidate.js +14 -0
  11. package/dist/api/annotation-target-candidate.js.map +1 -0
  12. package/dist/api/annotation-target-cloning.d.ts +5 -0
  13. package/dist/api/annotation-target-cloning.d.ts.map +1 -0
  14. package/dist/api/annotation-target-cloning.js +122 -0
  15. package/dist/api/annotation-target-cloning.js.map +1 -0
  16. package/dist/api/annotation-target-diagnostics.d.ts +8 -0
  17. package/dist/api/annotation-target-diagnostics.d.ts.map +1 -0
  18. package/dist/api/annotation-target-diagnostics.js +57 -0
  19. package/dist/api/annotation-target-diagnostics.js.map +1 -0
  20. package/dist/api/annotation-target-validation.d.ts +2 -2
  21. package/dist/api/annotation-target-validation.d.ts.map +1 -1
  22. package/dist/api/annotation-target-validation.js +5 -272
  23. package/dist/api/annotation-target-validation.js.map +1 -1
  24. package/dist/api/contracts.d.ts +2 -1
  25. package/dist/api/contracts.d.ts.map +1 -1
  26. package/dist/api/contracts.js +1 -0
  27. package/dist/api/contracts.js.map +1 -1
  28. package/dist/api/declarative-validation.d.ts +13 -0
  29. package/dist/api/declarative-validation.d.ts.map +1 -0
  30. package/dist/api/declarative-validation.js +63 -0
  31. package/dist/api/declarative-validation.js.map +1 -0
  32. package/dist/api/document-queries.d.ts.map +1 -1
  33. package/dist/api/document-queries.js +74 -3
  34. package/dist/api/document-queries.js.map +1 -1
  35. package/dist/api/document.d.ts +44 -0
  36. package/dist/api/document.d.ts.map +1 -1
  37. package/dist/api/engine-node-attributes.d.ts +15 -0
  38. package/dist/api/engine-node-attributes.d.ts.map +1 -0
  39. package/dist/api/engine-node-attributes.js +99 -0
  40. package/dist/api/engine-node-attributes.js.map +1 -0
  41. package/dist/api/normalize.d.ts.map +1 -1
  42. package/dist/api/normalize.js +22 -1
  43. package/dist/api/normalize.js.map +1 -1
  44. package/dist/api/serialize.js +2 -14
  45. package/dist/api/serialize.js.map +1 -1
  46. package/dist/cli/args.d.ts +6 -2
  47. package/dist/cli/args.d.ts.map +1 -1
  48. package/dist/cli/args.js +33 -25
  49. package/dist/cli/args.js.map +1 -1
  50. package/dist/cli/declarative-validation.d.ts +18 -0
  51. package/dist/cli/declarative-validation.d.ts.map +1 -0
  52. package/dist/cli/declarative-validation.js +131 -0
  53. package/dist/cli/declarative-validation.js.map +1 -0
  54. package/dist/cli/files.d.ts +1 -0
  55. package/dist/cli/files.d.ts.map +1 -1
  56. package/dist/cli/files.js +3 -0
  57. package/dist/cli/files.js.map +1 -1
  58. package/dist/cli/run.d.ts.map +1 -1
  59. package/dist/cli/run.js +18 -3
  60. package/dist/cli/run.js.map +1 -1
  61. package/dist/cli/validate-args.d.ts +18 -0
  62. package/dist/cli/validate-args.d.ts.map +1 -0
  63. package/dist/cli/validate-args.js +131 -0
  64. package/dist/cli/validate-args.js.map +1 -0
  65. package/dist/declarative-validation/assertions/context.d.ts +8 -0
  66. package/dist/declarative-validation/assertions/context.d.ts.map +1 -0
  67. package/dist/declarative-validation/assertions/context.js +2 -0
  68. package/dist/declarative-validation/assertions/context.js.map +1 -0
  69. package/dist/declarative-validation/assertions/diagnostics.d.ts +22 -0
  70. package/dist/declarative-validation/assertions/diagnostics.d.ts.map +1 -0
  71. package/dist/declarative-validation/assertions/diagnostics.js +71 -0
  72. package/dist/declarative-validation/assertions/diagnostics.js.map +1 -0
  73. package/dist/declarative-validation/assertions/document-text-offsets.d.ts +3 -0
  74. package/dist/declarative-validation/assertions/document-text-offsets.d.ts.map +1 -0
  75. package/dist/declarative-validation/assertions/document-text-offsets.js +32 -0
  76. package/dist/declarative-validation/assertions/document-text-offsets.js.map +1 -0
  77. package/dist/declarative-validation/assertions/evaluator.d.ts +5 -0
  78. package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -0
  79. package/dist/declarative-validation/assertions/evaluator.js +46 -0
  80. package/dist/declarative-validation/assertions/evaluator.js.map +1 -0
  81. package/dist/declarative-validation/assertions/exists.d.ts +4 -0
  82. package/dist/declarative-validation/assertions/exists.d.ts.map +1 -0
  83. package/dist/declarative-validation/assertions/exists.js +8 -0
  84. package/dist/declarative-validation/assertions/exists.js.map +1 -0
  85. package/dist/declarative-validation/assertions/frontmatter-required.d.ts +9 -0
  86. package/dist/declarative-validation/assertions/frontmatter-required.d.ts.map +1 -0
  87. package/dist/declarative-validation/assertions/frontmatter-required.js +15 -0
  88. package/dist/declarative-validation/assertions/frontmatter-required.js.map +1 -0
  89. package/dist/declarative-validation/assertions/id-targets.d.ts +29 -0
  90. package/dist/declarative-validation/assertions/id-targets.d.ts.map +1 -0
  91. package/dist/declarative-validation/assertions/id-targets.js +428 -0
  92. package/dist/declarative-validation/assertions/id-targets.js.map +1 -0
  93. package/dist/declarative-validation/assertions/id-tokens.d.ts +15 -0
  94. package/dist/declarative-validation/assertions/id-tokens.d.ts.map +1 -0
  95. package/dist/declarative-validation/assertions/id-tokens.js +27 -0
  96. package/dist/declarative-validation/assertions/id-tokens.js.map +1 -0
  97. package/dist/declarative-validation/assertions/ids.d.ts +9 -0
  98. package/dist/declarative-validation/assertions/ids.d.ts.map +1 -0
  99. package/dist/declarative-validation/assertions/ids.js +42 -0
  100. package/dist/declarative-validation/assertions/ids.js.map +1 -0
  101. package/dist/declarative-validation/assertions/index.d.ts +3 -0
  102. package/dist/declarative-validation/assertions/index.d.ts.map +1 -0
  103. package/dist/declarative-validation/assertions/index.js +3 -0
  104. package/dist/declarative-validation/assertions/index.js.map +1 -0
  105. package/dist/declarative-validation/assertions/literal-text.d.ts +2 -0
  106. package/dist/declarative-validation/assertions/literal-text.d.ts.map +1 -0
  107. package/dist/declarative-validation/assertions/literal-text.js +14 -0
  108. package/dist/declarative-validation/assertions/literal-text.js.map +1 -0
  109. package/dist/declarative-validation/assertions/normalized-source-ranges.d.ts +3 -0
  110. package/dist/declarative-validation/assertions/normalized-source-ranges.d.ts.map +1 -0
  111. package/dist/declarative-validation/assertions/normalized-source-ranges.js +105 -0
  112. package/dist/declarative-validation/assertions/normalized-source-ranges.js.map +1 -0
  113. package/dist/declarative-validation/assertions/ordering.d.ts +6 -0
  114. package/dist/declarative-validation/assertions/ordering.d.ts.map +1 -0
  115. package/dist/declarative-validation/assertions/ordering.js +71 -0
  116. package/dist/declarative-validation/assertions/ordering.js.map +1 -0
  117. package/dist/declarative-validation/assertions/references.d.ts +9 -0
  118. package/dist/declarative-validation/assertions/references.d.ts.map +1 -0
  119. package/dist/declarative-validation/assertions/references.js +354 -0
  120. package/dist/declarative-validation/assertions/references.js.map +1 -0
  121. package/dist/declarative-validation/assertions/sections-required.d.ts +9 -0
  122. package/dist/declarative-validation/assertions/sections-required.d.ts.map +1 -0
  123. package/dist/declarative-validation/assertions/sections-required.js +49 -0
  124. package/dist/declarative-validation/assertions/sections-required.js.map +1 -0
  125. package/dist/declarative-validation/assertions/table-columns-required.d.ts +9 -0
  126. package/dist/declarative-validation/assertions/table-columns-required.d.ts.map +1 -0
  127. package/dist/declarative-validation/assertions/table-columns-required.js +31 -0
  128. package/dist/declarative-validation/assertions/table-columns-required.js.map +1 -0
  129. package/dist/declarative-validation/assertions/text-length.d.ts +9 -0
  130. package/dist/declarative-validation/assertions/text-length.d.ts.map +1 -0
  131. package/dist/declarative-validation/assertions/text-length.js +40 -0
  132. package/dist/declarative-validation/assertions/text-length.js.map +1 -0
  133. package/dist/declarative-validation/assertions/text-occurrence-count.d.ts +9 -0
  134. package/dist/declarative-validation/assertions/text-occurrence-count.d.ts.map +1 -0
  135. package/dist/declarative-validation/assertions/text-occurrence-count.js +22 -0
  136. package/dist/declarative-validation/assertions/text-occurrence-count.js.map +1 -0
  137. package/dist/declarative-validation/assertions/text.d.ts +9 -0
  138. package/dist/declarative-validation/assertions/text.d.ts.map +1 -0
  139. package/dist/declarative-validation/assertions/text.js +32 -0
  140. package/dist/declarative-validation/assertions/text.js.map +1 -0
  141. package/dist/declarative-validation/compiler/assertion-builders.d.ts +6 -0
  142. package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -0
  143. package/dist/declarative-validation/compiler/assertion-builders.js +199 -0
  144. package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -0
  145. package/dist/declarative-validation/compiler/assertion-shapes.d.ts +18 -0
  146. package/dist/declarative-validation/compiler/assertion-shapes.d.ts.map +1 -0
  147. package/dist/declarative-validation/compiler/assertion-shapes.js +129 -0
  148. package/dist/declarative-validation/compiler/assertion-shapes.js.map +1 -0
  149. package/dist/declarative-validation/compiler/assertions.d.ts +5 -0
  150. package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -0
  151. package/dist/declarative-validation/compiler/assertions.js +25 -0
  152. package/dist/declarative-validation/compiler/assertions.js.map +1 -0
  153. package/dist/declarative-validation/compiler/compatibility.d.ts +4 -0
  154. package/dist/declarative-validation/compiler/compatibility.d.ts.map +1 -0
  155. package/dist/declarative-validation/compiler/compatibility.js +28 -0
  156. package/dist/declarative-validation/compiler/compatibility.js.map +1 -0
  157. package/dist/declarative-validation/compiler/diagnostics.d.ts +3 -0
  158. package/dist/declarative-validation/compiler/diagnostics.d.ts.map +1 -0
  159. package/dist/declarative-validation/compiler/diagnostics.js +9 -0
  160. package/dist/declarative-validation/compiler/diagnostics.js.map +1 -0
  161. package/dist/declarative-validation/compiler/index.d.ts +5 -0
  162. package/dist/declarative-validation/compiler/index.d.ts.map +1 -0
  163. package/dist/declarative-validation/compiler/index.js +164 -0
  164. package/dist/declarative-validation/compiler/index.js.map +1 -0
  165. package/dist/declarative-validation/compiler/plan.d.ts +58 -0
  166. package/dist/declarative-validation/compiler/plan.d.ts.map +1 -0
  167. package/dist/declarative-validation/compiler/plan.js +2 -0
  168. package/dist/declarative-validation/compiler/plan.js.map +1 -0
  169. package/dist/declarative-validation/diagnostics/index.d.ts +7 -0
  170. package/dist/declarative-validation/diagnostics/index.d.ts.map +1 -0
  171. package/dist/declarative-validation/diagnostics/index.js +2 -0
  172. package/dist/declarative-validation/diagnostics/index.js.map +1 -0
  173. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.d.ts +11 -0
  174. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.d.ts.map +1 -0
  175. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.js +45 -0
  176. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.js.map +1 -0
  177. package/dist/declarative-validation/evidence/index.d.ts +14 -0
  178. package/dist/declarative-validation/evidence/index.d.ts.map +1 -0
  179. package/dist/declarative-validation/evidence/index.js +38 -0
  180. package/dist/declarative-validation/evidence/index.js.map +1 -0
  181. package/dist/declarative-validation/profile/assertion-schema.d.ts +4 -0
  182. package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -0
  183. package/dist/declarative-validation/profile/assertion-schema.js +285 -0
  184. package/dist/declarative-validation/profile/assertion-schema.js.map +1 -0
  185. package/dist/declarative-validation/profile/data-closure.d.ts +6 -0
  186. package/dist/declarative-validation/profile/data-closure.d.ts.map +1 -0
  187. package/dist/declarative-validation/profile/data-closure.js +167 -0
  188. package/dist/declarative-validation/profile/data-closure.js.map +1 -0
  189. package/dist/declarative-validation/profile/direct-profile-diagnostics.d.ts +5 -0
  190. package/dist/declarative-validation/profile/direct-profile-diagnostics.d.ts.map +1 -0
  191. package/dist/declarative-validation/profile/direct-profile-diagnostics.js +73 -0
  192. package/dist/declarative-validation/profile/direct-profile-diagnostics.js.map +1 -0
  193. package/dist/declarative-validation/profile/index.d.ts +113 -0
  194. package/dist/declarative-validation/profile/index.d.ts.map +1 -0
  195. package/dist/declarative-validation/profile/index.js +2 -0
  196. package/dist/declarative-validation/profile/index.js.map +1 -0
  197. package/dist/declarative-validation/profile/materialization.d.ts +9 -0
  198. package/dist/declarative-validation/profile/materialization.d.ts.map +1 -0
  199. package/dist/declarative-validation/profile/materialization.js +109 -0
  200. package/dist/declarative-validation/profile/materialization.js.map +1 -0
  201. package/dist/declarative-validation/profile/parse.d.ts +3 -0
  202. package/dist/declarative-validation/profile/parse.d.ts.map +1 -0
  203. package/dist/declarative-validation/profile/parse.js +89 -0
  204. package/dist/declarative-validation/profile/parse.js.map +1 -0
  205. package/dist/declarative-validation/profile/schema-values.d.ts +14 -0
  206. package/dist/declarative-validation/profile/schema-values.d.ts.map +1 -0
  207. package/dist/declarative-validation/profile/schema-values.js +81 -0
  208. package/dist/declarative-validation/profile/schema-values.js.map +1 -0
  209. package/dist/declarative-validation/profile/schema.d.ts +9 -0
  210. package/dist/declarative-validation/profile/schema.d.ts.map +1 -0
  211. package/dist/declarative-validation/profile/schema.js +98 -0
  212. package/dist/declarative-validation/profile/schema.js.map +1 -0
  213. package/dist/declarative-validation/profile/selector-schema.d.ts +4 -0
  214. package/dist/declarative-validation/profile/selector-schema.d.ts.map +1 -0
  215. package/dist/declarative-validation/profile/selector-schema.js +145 -0
  216. package/dist/declarative-validation/profile/selector-schema.js.map +1 -0
  217. package/dist/declarative-validation/results/index.d.ts +26 -0
  218. package/dist/declarative-validation/results/index.d.ts.map +1 -0
  219. package/dist/declarative-validation/results/index.js +2 -0
  220. package/dist/declarative-validation/results/index.js.map +1 -0
  221. package/dist/declarative-validation/selectors/index.d.ts +57 -0
  222. package/dist/declarative-validation/selectors/index.d.ts.map +1 -0
  223. package/dist/declarative-validation/selectors/index.js +149 -0
  224. package/dist/declarative-validation/selectors/index.js.map +1 -0
  225. package/dist/declarative-validation/selectors/source.d.ts +8 -0
  226. package/dist/declarative-validation/selectors/source.d.ts.map +1 -0
  227. package/dist/declarative-validation/selectors/source.js +57 -0
  228. package/dist/declarative-validation/selectors/source.js.map +1 -0
  229. package/dist/declarative-validation/selectors/table-targets.d.ts +13 -0
  230. package/dist/declarative-validation/selectors/table-targets.d.ts.map +1 -0
  231. package/dist/declarative-validation/selectors/table-targets.js +106 -0
  232. package/dist/declarative-validation/selectors/table-targets.js.map +1 -0
  233. package/dist/declarative-validation/selectors/table-text.d.ts +4 -0
  234. package/dist/declarative-validation/selectors/table-text.d.ts.map +1 -0
  235. package/dist/declarative-validation/selectors/table-text.js +16 -0
  236. package/dist/declarative-validation/selectors/table-text.js.map +1 -0
  237. package/dist/{ir → internal}/document-node-walk.d.ts +5 -0
  238. package/dist/internal/document-node-walk.d.ts.map +1 -0
  239. package/dist/internal/document-node-walk.js +36 -0
  240. package/dist/internal/document-node-walk.js.map +1 -0
  241. package/dist/internal/stable-json.d.ts +3 -0
  242. package/dist/internal/stable-json.d.ts.map +1 -0
  243. package/dist/internal/stable-json.js +17 -0
  244. package/dist/internal/stable-json.js.map +1 -0
  245. package/dist/ir/document-derived-views.d.ts.map +1 -1
  246. package/dist/ir/document-derived-views.js +2 -0
  247. package/dist/ir/document-derived-views.js.map +1 -1
  248. package/dist/ir/document-link-reference-views.d.ts +3 -0
  249. package/dist/ir/document-link-reference-views.d.ts.map +1 -0
  250. package/dist/ir/document-link-reference-views.js +150 -0
  251. package/dist/ir/document-link-reference-views.js.map +1 -0
  252. package/dist/ir/document-link-views.d.ts.map +1 -1
  253. package/dist/ir/document-link-views.js +7 -8
  254. package/dist/ir/document-link-views.js.map +1 -1
  255. package/dist/ir/document-list-views.d.ts.map +1 -1
  256. package/dist/ir/document-list-views.js +14 -13
  257. package/dist/ir/document-list-views.js.map +1 -1
  258. package/dist/ir/document-sections.d.ts.map +1 -1
  259. package/dist/ir/document-sections.js +2 -4
  260. package/dist/ir/document-sections.js.map +1 -1
  261. package/dist/ir/document-table-views.js +1 -1
  262. package/dist/ir/document-table-views.js.map +1 -1
  263. package/dist/ir/document-text-spans.js +1 -1
  264. package/dist/ir/document-text-spans.js.map +1 -1
  265. package/dist/ir/document.d.ts +2 -3
  266. package/dist/ir/document.d.ts.map +1 -1
  267. package/dist/ir/document.js +1 -1
  268. package/dist/ir/document.js.map +1 -1
  269. package/dist/ir/index.d.ts +1 -0
  270. package/dist/ir/index.d.ts.map +1 -1
  271. package/dist/ir/normalization-input.d.ts +12 -0
  272. package/dist/ir/normalization-input.d.ts.map +1 -0
  273. package/dist/ir/normalization-input.js +2 -0
  274. package/dist/ir/normalization-input.js.map +1 -0
  275. package/dist/rules/code-fence-languages.d.ts.map +1 -1
  276. package/dist/rules/code-fence-languages.js +4 -6
  277. package/dist/rules/code-fence-languages.js.map +1 -1
  278. package/dist/rules/document-query.d.ts +0 -2
  279. package/dist/rules/document-query.d.ts.map +1 -1
  280. package/dist/rules/document-query.js +1 -17
  281. package/dist/rules/document-query.js.map +1 -1
  282. package/dist/rules/index.d.ts +2 -6
  283. package/dist/rules/index.d.ts.map +1 -1
  284. package/dist/rules/index.js +3 -37
  285. package/dist/rules/index.js.map +1 -1
  286. package/dist/rules/links-allowed-schemes.d.ts.map +1 -1
  287. package/dist/rules/links-allowed-schemes.js +3 -2
  288. package/dist/rules/links-allowed-schemes.js.map +1 -1
  289. package/dist/rules/registry.d.ts +12 -0
  290. package/dist/rules/registry.d.ts.map +1 -0
  291. package/dist/rules/registry.js +61 -0
  292. package/dist/rules/registry.js.map +1 -0
  293. package/docs/contracts/api.md +661 -0
  294. package/docs/contracts/declarative-validation.md +559 -0
  295. package/docs/contracts/frontmatter.md +122 -0
  296. package/fixtures/declarative-validation/examples/operational-spec/fail.md +32 -0
  297. package/fixtures/declarative-validation/examples/operational-spec/pass.md +34 -0
  298. package/fixtures/declarative-validation/examples/operational-spec/profile.yaml +131 -0
  299. package/fixtures/declarative-validation/examples/release-checklist/fail.md +22 -0
  300. package/fixtures/declarative-validation/examples/release-checklist/pass.md +22 -0
  301. package/fixtures/declarative-validation/examples/release-checklist/profile.yaml +96 -0
  302. package/fixtures/declarative-validation/examples/requirements-traceability/fail.md +27 -0
  303. package/fixtures/declarative-validation/examples/requirements-traceability/pass.md +27 -0
  304. package/fixtures/declarative-validation/examples/requirements-traceability/profile.yaml +119 -0
  305. package/package.json +21 -6
  306. package/dist/ir/document-node-walk.d.ts.map +0 -1
  307. package/dist/ir/document-node-walk.js +0 -16
  308. package/dist/ir/document-node-walk.js.map +0 -1
@@ -0,0 +1,661 @@
1
+ # Public API Contract
2
+
3
+ Status: package 2.0.0, document contract 1.0.0
4
+ Last updated: 2026-05-13
5
+
6
+ This document defines the public `@jasonbelmonti/markdown-engine` package
7
+ contract for the `2.0.0` package release. The serialized rich IR document
8
+ contract remains `documentVersion: "1.0.0"`. The stable public surface is the
9
+ package export from `@jasonbelmonti/markdown-engine`, not internal adapter
10
+ modules or raw parser output. The 1.0 rich IR design is tracked in
11
+ `docs/design/markdown-engine-1.0-rich-ir-operational-design-spec.md`.
12
+
13
+ ## Exported Surface
14
+
15
+ The package root exports the API functions, helpers, and types from `src/api/**`:
16
+
17
+ - `parse(markdown, options?)`
18
+ - `normalize(parsed, options?)`
19
+ - `validate(document, config?, options?)`
20
+ - `serialize(result, options?)`
21
+ - `documentQueries`
22
+ - `validateAnnotations(document, annotations)`
23
+ - `parseValidationProfile(input, options?)`
24
+ - `validateWithProfile(document, profile, options?)`
25
+
26
+ The package root also exports the public result, document, diagnostic, config,
27
+ and function types declared in `src/api/**`.
28
+
29
+ The following implementation details are internal and are not stable public
30
+ contracts:
31
+
32
+ - raw mdast/unified parser AST nodes
33
+ - raw parser `position` fields
34
+ - raw `yaml` parser documents, CST, tokens, warnings, and errors
35
+ - internal parser, frontmatter, config-loader, IR-normalizer, and rule-registry
36
+ modules
37
+
38
+ ## `parse`
39
+
40
+ Signature:
41
+
42
+ ```ts
43
+ parse(markdown: string, options?: ParseOptions): ParseResult
44
+ ```
45
+
46
+ `ParseOptions.path` is optional. When present, the path is copied into the
47
+ parsed result and normalized engine document.
48
+
49
+ `ParseResult` contains:
50
+
51
+ - `parsed`: the engine-owned parsed Markdown value
52
+ - `diagnostics`: all parse/frontmatter diagnostics produced by parsing
53
+
54
+ `ParsedMarkdown` contains:
55
+
56
+ - `markdown`: the original input string
57
+ - `body`: Markdown body content after frontmatter extraction
58
+ - `path`: optional caller-supplied path
59
+ - `frontmatter`: JSON-safe parsed YAML value when frontmatter is present
60
+ - `document`: an engine-owned `EngineDocument`
61
+ - `diagnostics`: parse/frontmatter diagnostics
62
+
63
+ Absent frontmatter omits `frontmatter`. Empty frontmatter produces `{}`.
64
+ Frontmatter YAML behavior is defined by `docs/contracts/frontmatter.md`.
65
+
66
+ ## `normalize`
67
+
68
+ Signature:
69
+
70
+ ```ts
71
+ normalize(parsed: ParsedMarkdown, options?: NormalizeOptions): NormalizeResult
72
+ ```
73
+
74
+ `NormalizeOptions.documentVersion` selects the document contract version.
75
+ Package 2.0 defaults omitted `documentVersion` to the rich IR
76
+ `"1.0.0"` contract. The retained `0.1.0`-compatible path is `"0.0.0"` and must
77
+ be requested explicitly.
78
+
79
+ `NormalizeOptions.preserveSourceLocations` defaults to `true`. When set to
80
+ `false`, source ranges and source slices are omitted from the normalized
81
+ document, but deterministic node target IDs are still generated for the 1.0
82
+ path.
83
+
84
+ `NormalizeResult` contains:
85
+
86
+ - `document`: a cloned and normalized `EngineDocument`
87
+ - `diagnostics`: cloned diagnostics from the parsed input
88
+
89
+ Normalization sorts object attribute keys, clones public values, preserves
90
+ frontmatter when present, and does not expose raw parser AST data.
91
+
92
+ ## `validate`
93
+
94
+ Signature:
95
+
96
+ ```ts
97
+ validate(
98
+ document: EngineDocument,
99
+ config?: ValidationConfig,
100
+ options?: ValidateOptions,
101
+ ): ValidationResult
102
+ ```
103
+
104
+ `ValidationConfig` is YAML-friendly and currently supports a `rules` object.
105
+ The supported deterministic rule families in this contract slice are:
106
+
107
+ - `codeFences.languages`
108
+ - `frontmatter.required`
109
+ - `headings.required`
110
+ - `links.allowedSchemes`
111
+ - `rawHtml.policy`
112
+
113
+ `ValidateOptions.path` is accepted as a public option for API symmetry and
114
+ future diagnostics, but the current implementation does not emit additional
115
+ path-derived result fields from validation.
116
+
117
+ `codeFences.languages` configuration shape:
118
+
119
+ ```yaml
120
+ rules:
121
+ codeFences.languages:
122
+ allowed:
123
+ - ts
124
+ - bash
125
+ requireLanguage: true
126
+ severity: error
127
+ ```
128
+
129
+ `allowed` is optional when `requireLanguage` is `true`; when present it must be
130
+ a non-empty array of non-empty strings. `requireLanguage` is optional and
131
+ defaults to `false`. At least one of `allowed` or `requireLanguage` must be
132
+ configured. `severity` is optional and defaults to `error`; allowed values are
133
+ `error`, `warning`, and `info`.
134
+
135
+ `frontmatter.required` configuration shape:
136
+
137
+ ```yaml
138
+ rules:
139
+ frontmatter.required:
140
+ fields:
141
+ - title
142
+ - owner
143
+ severity: error
144
+ ```
145
+
146
+ `fields` must be a non-empty array of non-empty strings. `severity` is optional
147
+ and defaults to `error`; allowed values are `error`, `warning`, and `info`.
148
+
149
+ `headings.required` configuration shape:
150
+
151
+ ```yaml
152
+ rules:
153
+ headings.required:
154
+ headings:
155
+ - Objective
156
+ - Success Criteria
157
+ severity: error
158
+ ```
159
+
160
+ `headings` must be a non-empty array of non-empty strings. The rule checks
161
+ normalized heading text. `severity` is optional and defaults to `error`;
162
+ allowed values are `error`, `warning`, and `info`.
163
+
164
+ `links.allowedSchemes` configuration shape:
165
+
166
+ ```yaml
167
+ rules:
168
+ links.allowedSchemes:
169
+ schemes:
170
+ - https
171
+ - mailto
172
+ severity: error
173
+ ```
174
+
175
+ `schemes` must be a non-empty array of non-empty strings. URL schemes are
176
+ compared case-insensitively. Relative URLs without a scheme do not produce
177
+ diagnostics. `severity` is optional and defaults to `error`; allowed values are
178
+ `error`, `warning`, and `info`.
179
+
180
+ `rawHtml.policy` configuration shape:
181
+
182
+ ```yaml
183
+ rules:
184
+ rawHtml.policy:
185
+ policy: deny
186
+ ```
187
+
188
+ `policy` must be `allow`, `warn`, or `deny`. `allow` emits no diagnostics.
189
+ `warn` emits warning diagnostics for raw HTML nodes and does not make the
190
+ validation result invalid. `deny` emits error diagnostics for raw HTML nodes.
191
+ The package still treats raw HTML as inert data; it does not execute, render,
192
+ sanitize, fetch, or evaluate HTML.
193
+
194
+ Unsupported rules produce `config.rule.unsupported` diagnostics. Invalid config
195
+ shape produces config diagnostics. Unsupported rules are not inferred,
196
+ executed, or delegated to semantic evaluation.
197
+
198
+ `ValidationResult` contains:
199
+
200
+ - `valid`: `false` when any error-severity diagnostic exists
201
+ - `diagnostics`: top-level validation diagnostics
202
+ - `ruleResults`: per-rule deterministic results
203
+
204
+ Each `ValidationRuleResult` contains `ruleId`, `passed`, and `diagnostics`.
205
+ `passed` is `false` when the rule emits diagnostics, including warning or info
206
+ diagnostics. `valid` is controlled by error-severity diagnostics only.
207
+
208
+ ## Declarative Validation
209
+
210
+ The complete declarative validation syntax, CLI, diagnostic, evidence, and
211
+ boundary contract is defined in `docs/contracts/declarative-validation.md`.
212
+
213
+ Signatures:
214
+
215
+ ```ts
216
+ parseValidationProfile(
217
+ input: string | JsonSafeValue,
218
+ options?: DeclarativeProfileParseOptions,
219
+ ): DeclarativeProfileParseResult
220
+
221
+ validateWithProfile(
222
+ document: EngineDocument,
223
+ profile: ValidationProfile,
224
+ options?: DeclarativeValidationOptions,
225
+ ): DeclarativeValidationResult
226
+ ```
227
+
228
+ `parseValidationProfile` accepts YAML text or JSON-safe profile objects. The
229
+ top-level profile keys are `syntaxVersion`, `documentVersion`, and `rules`.
230
+ `syntaxVersion` must be `"markdown-engine.validation@v1"`.
231
+ `documentVersion` is optional; when provided it must be `"0.0.0"` or
232
+ `"1.0.0"`. The parser preserves omission and does not inject a default into
233
+ the parsed profile.
234
+
235
+ `validateWithProfile` resolves an omitted profile `documentVersion` to the
236
+ supplied normalized `EngineDocument.version`. The returned
237
+ `DeclarativeValidationResult.profile.documentVersion` records that resolved
238
+ version. If an explicit profile `documentVersion` does not match
239
+ `document.version`, validation emits `profile.config.documentVersionMismatch`,
240
+ returns no rule results, and does not evaluate rules.
241
+
242
+ When `DeclarativeValidationOptions.includeEvidence` is `true`, the result
243
+ contains deterministic evidence. `inputHash` hashes the canonical supplied
244
+ `EngineDocument` without top-level `document.path`. `profileHash` hashes the
245
+ resolved profile after applying the `documentVersion` and rule `severity`
246
+ defaults, so an omitted `documentVersion` and an explicit matching
247
+ `documentVersion` produce the same profile hash for the same document version
248
+ and rules.
249
+
250
+ ## `serialize`
251
+
252
+ Signature:
253
+
254
+ ```ts
255
+ serialize(
256
+ result:
257
+ | ParseResult
258
+ | NormalizeResult
259
+ | ValidationResult
260
+ | DeclarativeValidationResult
261
+ | EngineDocument
262
+ | AnnotationValidationResult,
263
+ options?: SerializeOptions,
264
+ ): string
265
+ ```
266
+
267
+ `SerializeOptions.pretty` controls two-space JSON formatting. Serialization
268
+ normalizes plain object key order, recursively normalizes arrays and objects,
269
+ and omits `undefined` properties.
270
+
271
+ `SerializeOptions.compatibilityMode` is optional. When provided, it verifies
272
+ document-bearing public results before serialization:
273
+
274
+ - `compatibilityMode: "default"` expects document version `"1.0.0"`.
275
+ - `compatibilityMode: "legacy-0.1"` expects document version `"0.0.0"`.
276
+
277
+ Mismatched document-bearing results throw `EngineCompatibilityError` with code
278
+ `engine.compatibility.versionMismatch`, `requestedMode`, `expectedVersion`, and
279
+ `actualVersion`. Results that do not contain an `EngineDocument`, such as the
280
+ current `ValidationResult`, are not rejected by the compatibility check.
281
+
282
+ The serializer is intended for stable JSON review evidence and downstream
283
+ contract checks. It does not accept arbitrary class instances as a public data
284
+ model.
285
+
286
+ ## Document Contract
287
+
288
+ `EngineDocument` contains:
289
+
290
+ - `kind`: currently `"markdown-document"`
291
+ - `version`: currently `"0.0.0"`
292
+ - `path`: optional caller-supplied path
293
+ - `frontmatter`: JSON-safe parsed frontmatter value when present
294
+ - `children`: normalized `EngineNode[]`
295
+ - `sourceRange`: optional source range
296
+
297
+ `EngineNode` contains:
298
+
299
+ - `type`: engine-owned node type string
300
+ - `text`: optional text content
301
+ - `attributes`: optional JSON-safe attributes
302
+ - `sourceRange`: optional source range
303
+ - `children`: optional child nodes
304
+
305
+ Node type coverage remains limited to the current parser/IR implementation and
306
+ will expand through implementation work packages. Code nodes may include a
307
+ `kind` attribute of `fenced` or `indented`; `codeFences.*` rules apply only to
308
+ code nodes with `kind: "fenced"`. Raw parser node objects are not public.
309
+
310
+ ## 1.0 Contract
311
+
312
+ The final 1.0 document contract is selected by default in package 2.0, remains
313
+ available explicitly as `documentVersion: "1.0.0"`, and is checked with
314
+ `compatibilityMode: "default"`.
315
+
316
+ Callers select the 1.0 contract with either `normalize(parsed)` or an explicit
317
+ selector:
318
+
319
+ ```ts
320
+ const parsed = parse(markdown, { path: "mission.md" });
321
+ const document = normalize(parsed.parsed, {
322
+ documentVersion: "1.0.0",
323
+ }).document;
324
+
325
+ const sections = documentQueries.sections(document);
326
+ const serialized = serialize(document, { compatibilityMode: "default" });
327
+ ```
328
+
329
+ The retained `0.1.0`-compatible path remains explicit:
330
+
331
+ ```ts
332
+ const legacyDocument = normalize(parsed.parsed, {
333
+ documentVersion: "0.0.0",
334
+ }).document;
335
+
336
+ const serializedLegacy = serialize(legacyDocument, {
337
+ compatibilityMode: "legacy-0.1",
338
+ });
339
+ ```
340
+
341
+ ### 1.0 Document Fields
342
+
343
+ When callers normalize with `documentVersion: "1.0.0"`, the document
344
+ includes deterministic derived structural views:
345
+
346
+ - `kind`: `"markdown-document"`.
347
+ - `version`: `"1.0.0"`.
348
+ - `path`: optional caller-supplied path from parse or normalized document input.
349
+ - `frontmatter`: JSON-safe parsed frontmatter value when present.
350
+ - `target`: the document-level `EngineNodeTarget`.
351
+ - `children`: normalized `EngineNode[]`; each node has `target` in the 1.0
352
+ path.
353
+ - `sourceRange`: optional document source range when source locations are
354
+ preserved.
355
+ - `compatibility`: `{ mode: "default", reason: "1.0 document contract" }`.
356
+ - `sections`: heading-derived `EngineSection[]`.
357
+ - `textSpans`: text-bearing `EngineTextSpan[]`.
358
+ - `tables`: `EngineTable[]` with flattened table-cell coordinates.
359
+ - `lists`: `EngineList[]` with list item coordinates.
360
+ - `links`: `EngineLink[]`.
361
+ - `linkReferences`: `EngineLinkReference[]`.
362
+ - `annotations`: optional caller-owned annotations if a caller attaches a
363
+ validated annotation result to the document.
364
+
365
+ `EngineNode` keeps the `0.1.0` fields `type`, optional `text`, optional
366
+ `attributes`, optional `sourceRange`, and optional `children`. In the 1.0
367
+ path it may also include:
368
+
369
+ - `target`: deterministic `EngineNodeTarget`.
370
+ - `source`: `{ range, text }` when source locations are preserved and parser
371
+ offsets are usable.
372
+
373
+ ### Target Contract And Stability Limits
374
+
375
+ `EngineNodeTarget` contains:
376
+
377
+ - `kind`: currently `"node"`.
378
+ - `id`: deterministic target ID, such as `node:1.1:link` or
379
+ `section:node:0:heading`.
380
+ - `path`: optional zero-based structural path through `children`.
381
+ - `nodeType`: optional engine-owned node type, such as `"document"`,
382
+ `"heading"`, `"paragraph"`, `"link"`, or `"section"`.
383
+ - `sourceRange`: optional cloned source range when source locations are
384
+ preserved.
385
+
386
+ Compatibility-first target taxonomy decision: the serialized
387
+ `EngineNodeTarget` shape remains unchanged for the 1.0 release lane. Its
388
+ `kind` field is still `"node"` for document, ordinary node, and section
389
+ addresses, so callers must not treat `target.kind` as the semantic target
390
+ category. Runtime target category is resolved by the documented query helpers:
391
+
392
+ - `document`: the document-level `document.target`. It identifies the whole
393
+ normalized document, is not returned by `documentQueries.nodes`, and does not
394
+ currently produce a source slice because normalized documents do not retain
395
+ the complete source text.
396
+ - `node`: an actual recursive `EngineNode.target` in `document.children`.
397
+ `documentQueries.nodes(document, { targetId })` only resolves this category.
398
+ - `section`: an `EngineSection.target` derived from an owning heading target.
399
+ It is resolved through `documentQueries.sections`; `sourceSlice` returns the
400
+ owning heading source slice when available.
401
+
402
+ Target IDs are deterministic for identical Markdown input, parser behavior,
403
+ normalization options, package version, and runtime version. They are not a
404
+ promise of stability across arbitrary content edits, parser upgrades, or final
405
+ 1.0 contract promotion. They do not expose raw mdast nodes or raw parser
406
+ position objects.
407
+
408
+ `SourceRange` contains `start` and `end` positions. Each position has `line`,
409
+ `column`, and optional `offset`. Source slices are produced only when both
410
+ offsets are present, integers, ordered, non-negative, and contained by the
411
+ document source text.
412
+
413
+ ### Structural Views
414
+
415
+ `sections` contains heading-derived `EngineSection` records:
416
+
417
+ - `target`: section target whose ID is derived from the heading target.
418
+ - `headingTarget`: target for the owning heading node.
419
+ - `parentSection`: optional parent section target.
420
+ - `depth`: heading depth.
421
+ - `title`: normalized heading text.
422
+ - `bodyTargets`: node targets owned by the section body.
423
+ - `childSections`: child section targets.
424
+
425
+ `textSpans` contains `EngineTextSpan` records with `target`, `text`, and
426
+ optional `sourceRange`.
427
+
428
+ `tables` contains `EngineTable` records with `target` and flattened `cells`.
429
+ Each `EngineTableCell` exposes `target`, normalized `text`, zero-based
430
+ `rowIndex`, zero-based `columnIndex`, `header`, and optional `sourceRange`. The
431
+ GFM header row is row index `0`; body rows continue at `1`, `2`, and so on.
432
+
433
+ `lists` contains `EngineList` records with `target`, `ordered`, optional
434
+ `start`, and `items`. Each `EngineListItem` exposes `target`, zero-based
435
+ `itemIndex` within its immediate list container, zero-based `depth`, optional
436
+ `checked`, and optional `sourceRange`.
437
+
438
+ `links` contains `EngineLink` records with `target`, `url`, normalized `text`,
439
+ optional `title`, and optional `sourceRange`. It remains scoped to inline
440
+ Markdown links.
441
+
442
+ `linkReferences` contains additive `EngineLinkReference` records for public,
443
+ source-located URL and reference extraction. Records use preorder depth-first
444
+ document order for node-backed constructs and include:
445
+
446
+ - `target`: target for the Markdown construct location.
447
+ - `kind`: one of `"link"`, `"image"`, `"definition"`, `"linkReference"`, or
448
+ `"imageReference"`.
449
+ - `url`: direct URL for inline links, images, and definitions; resolved
450
+ definition URL for reference usages when a matching definition is known.
451
+ - `title`: direct or resolved optional title when available.
452
+ - `text`: user-visible text for links and link reference usages when available.
453
+ - `alt`: user-visible alt text for images and image reference usages when
454
+ available.
455
+ - `label`: Markdown label for definitions and reference usages when available.
456
+ - `identifier`: normalized Markdown identifier for definitions and reference
457
+ usages when available.
458
+ - `referenceType`: reference usage type, such as `"full"`, `"collapsed"`, or
459
+ `"shortcut"`, when available.
460
+ - `definitionTarget`: target of the matched link definition for resolved
461
+ reference usages.
462
+ - `sourceRange`: optional construct source range.
463
+
464
+ Reference usages and definitions are represented as separate records. Reference
465
+ usage records are joined to the first matching definition target and URL when
466
+ the parsed Markdown structure provides a match. The view only reports Markdown
467
+ constructs present in the normalized engine node tree; reference syntax that the
468
+ parser treats as plain text is not reported.
469
+
470
+ ### Query Helpers
471
+
472
+ `documentQueries` exposes deterministic helper methods over this public IR:
473
+
474
+ - `nodes(document, query?)` filters recursive nodes by node type or target ID.
475
+ Recursive node-backed helper results use preorder depth-first document order:
476
+ each node appears before its descendants, and descendants are exhausted before
477
+ the next sibling.
478
+ - `sections(document, query?)` filters sections by target ID, heading target
479
+ ID, parent section target ID, title, or depth.
480
+ - `textSpans(document, query?)` filters spans by target ID, node type, exact
481
+ text, or included text.
482
+ - `tables(document, query?)` filters table views by target ID.
483
+ - `lists(document, query?)` filters list views by target ID, ordered state, or
484
+ item depth.
485
+ - `links(document, query?)` filters link views by target ID, URL, or text.
486
+ - `linkReferences(document, query?)` filters link-like reference views by target
487
+ ID, kind, URL, text, alt text, label, identifier, reference type, or
488
+ definition target ID.
489
+ - `targetCategory(document, target)` returns `"document"`, `"node"`,
490
+ `"section"`, or `undefined` for targets that do not resolve in the document.
491
+ - `resolveTarget(document, target)` returns a category-specific resolution:
492
+ document target, ordinary node plus optional source slice, or section plus
493
+ optional owning-heading source slice.
494
+ - `sourceSlice(document, target)` returns the precomputed source slice for node
495
+ targets when parser offsets are present, integer, ordered, non-negative, and
496
+ in bounds. For section targets, it returns the source slice for the owning
497
+ heading target. For document targets, it returns `undefined` because complete
498
+ source text is not stored on `EngineDocument`. It returns `undefined` instead
499
+ of guessing when offsets are absent, non-integer, reversed, negative,
500
+ unsupported, out of bounds, or when the target does not resolve.
501
+
502
+ ### Annotation Contract
503
+
504
+ Annotations use an explicit address-mode wrapper:
505
+
506
+ ```ts
507
+ type EngineAnnotationTarget =
508
+ | { kind: "node"; nodeTarget: EngineNodeTarget }
509
+ | { kind: "source"; sourceRange: SourceRange };
510
+ ```
511
+
512
+ For node annotations, `kind: "node"` identifies the annotation addressing mode.
513
+ The annotated Markdown node type remains `nodeTarget.nodeType`, such as
514
+ `"heading"` or `"paragraph"`. For source annotations, `sourceRange` contains
515
+ the exact caller-provided range. Annotation `payload` values remain opaque and
516
+ caller-owned; the engine validates only target shape and target existence.
517
+
518
+ `validateAnnotations(document, annotations)` returns:
519
+
520
+ - `valid`: `true` when all annotation targets are accepted.
521
+ - `annotations`: cloned annotations with target data preserved.
522
+ - `diagnostics`: deterministic `EngineTargetDiagnostic[]`.
523
+
524
+ The annotation validator accepts all resolvable engine targets: the document
525
+ target, ordinary node targets, and section targets. These keep
526
+ `target.kind: "node"` for wire compatibility; target category is determined by
527
+ the resolver APIs above. For source targets, it verifies start/end shape and
528
+ ordering. When the normalized
529
+ document has `sourceRange`, source targets must be contained by that document
530
+ range; when `sourceRange` is absent, the validator cannot prove source-target
531
+ bounds and does not synthesize a document range. It rejects malformed target
532
+ wrappers, malformed node targets, unknown node targets, invalid source range
533
+ ordering, and source ranges proven out of bounds. It does not interpret,
534
+ normalize, validate, or serialize caller payload semantics beyond normal public
535
+ serialization behavior.
536
+
537
+ ### Compatibility And Migration
538
+
539
+ The current package version is `2.0.0`. The serialized document contract
540
+ version remains `"1.0.0"`. Package 2.0 selects that rich IR contract by
541
+ default for `normalize(parsed)`; callers may also request it explicitly with
542
+ `normalize(..., { documentVersion: "1.0.0" })`. Serialization gates check it
543
+ with `compatibilityMode: "default"`.
544
+
545
+ The retained compatibility selector is `compatibilityMode: "legacy-0.1"`,
546
+ which accepts document-bearing public results with `version: "0.0.0"`. This is
547
+ the documented 0.1.x-compatible behavior gate. Consumers should not infer
548
+ compatibility from the absence of rich IR fields.
549
+
550
+ Migration from the `0.1.0` document shape, or from pre-2.0 API callers that
551
+ depended on implicit legacy normalization, requires consumers to:
552
+
553
+ - use package 2.0's default `normalize(parsed)` rich IR output or request
554
+ `documentVersion: "1.0.0"` during normalization;
555
+ - read `target`, `sections`, `textSpans`, `tables`, `lists`, `links`,
556
+ `linkReferences`, and `source` from the normalized document instead of
557
+ re-deriving them from raw Markdown;
558
+ - use `documentQueries` for structural access rather than depending on internal
559
+ traversal helpers;
560
+ - use `validateAnnotations` for caller-owned node and source annotations;
561
+ - serialize document-bearing rich IR outputs with `compatibilityMode:
562
+ "default"` in gates that must reject legacy document versions;
563
+ - use `compatibilityMode: "legacy-0.1"` only for retained 0.1.x-compatible
564
+ parse or normalize outputs.
565
+
566
+ ### CLI Impact
567
+
568
+ The local CLI runs parse and normalization for one Markdown file and writes
569
+ pretty JSON. BEL-952 changes the CLI default output to the 1.0 rich IR
570
+ contract: `--file` and `--path` emit a normalized result whose
571
+ `document.version` is `"1.0.0"` and whose document includes derived rich
572
+ IR views such as `target`, `sections`, `textSpans`, `tables`, `lists`, and
573
+ `links` when present. The document also exposes `linkReferences` for
574
+ URL-bearing links, images, definitions, and source-located reference usages.
575
+
576
+ Legacy CLI output remains explicit:
577
+
578
+ ```sh
579
+ markdown-engine --document-version 0.0.0 --file mission.md
580
+ ```
581
+
582
+ The supported CLI selector values are:
583
+
584
+ - `--document-version 1.0.0`: 1.0 rich IR output, also the default
585
+ when the selector is omitted.
586
+ - `--document-version 0.0.0`: retained `0.1.0`-compatible normalized document
587
+ output without rich derived views.
588
+
589
+ The selector accepts spaced or assignment-form syntax, such as
590
+ `--document-version 0.0.0` or `--document-version=0.0.0`. Missing, invalid, or
591
+ repeated `--document-version` selectors exit with code `2` and usage text; an
592
+ empty assignment-form selector is treated as missing. Directory traversal
593
+ remains unsupported.
594
+
595
+ Declarative validation is exposed through a separate subcommand:
596
+
597
+ ```sh
598
+ markdown-engine validate --file mission.md --profile profile.yaml
599
+ ```
600
+
601
+ The validate subcommand always normalizes the Markdown document as
602
+ `document.version` `"1.0.0"` and does not accept `--document-version`.
603
+ `--format json` is the default and only supported validation format. It reads,
604
+ parses, and compile-preflights the profile before reading the Markdown file.
605
+ Profile parse/config/compile failures exit with code `1` and emit JSON with
606
+ `stage: "profile"`, empty `ruleResults`, no `profile`, and no `evidence`.
607
+ Usage errors, unsupported formats, unknown arguments, and local file read errors
608
+ exit with code `2`. Validation success exits with code `0`; validation or
609
+ normalization error diagnostics exit with code `1`. Validation JSON includes
610
+ `profile`, `ruleResults`, `diagnostics`, and `evidence`.
611
+
612
+ Semver classification: package 2.0 makes the rich IR contract the default API
613
+ normalization output while retaining the document contract version
614
+ `"1.0.0"`. This is breaking for API consumers that call `normalize(parsed)` and
615
+ expect the legacy `0.0.0` document shape. Migration is to either consume the
616
+ rich IR fields or pin `documentVersion: "0.0.0"` until the downstream consumer
617
+ is ready. CLI consumers can still pin `--document-version 0.0.0` for explicit
618
+ legacy output.
619
+
620
+ ### Non-Goals And Limits
621
+
622
+ Structural views are derived from engine-owned document nodes, targets, and
623
+ source metadata. They do not expose raw parser AST fields as public contract.
624
+
625
+ The package boundary remains domain-neutral. The 1.0 contract does not
626
+ implement SpecTrace entities, profile compiler behavior, runtime lenses, MCP
627
+ transport, agent adapters, semantic or LLM evaluation, arbitrary rule plugins,
628
+ network services, persistence, file watching, graph storage, rendering,
629
+ sanitization, fetching, or raw HTML execution.
630
+
631
+ Source text and raw HTML remain inert strings. The engine does not promise
632
+ source slices when parser offsets are missing or unusable, and it does not
633
+ promise node target stability across arbitrary edits.
634
+
635
+ ## Diagnostic Contract
636
+
637
+ `MarkdownDiagnostic` contains:
638
+
639
+ - `code`: stable diagnostic code string
640
+ - `ruleId`: optional validation rule identifier
641
+ - `message`: human-readable diagnostic message
642
+ - `severity`: `"error"`, `"warning"`, or `"info"`
643
+ - `sourceRange`: optional source range
644
+
645
+ `SourceRange` contains `start` and `end` positions. Each position has `line`,
646
+ `column`, and optional `offset`.
647
+
648
+ ## Compatibility Notes
649
+
650
+ The `0.1.0` contract was review-gated by WP-2, MS-2, and MS-3 before first
651
+ publication. From the published `0.1.0` baseline forward, changes to public API
652
+ signatures, result fields, diagnostic schema, source-location semantics,
653
+ validation config semantics, or serialized output shape require
654
+ semantic-version classification. The planned 1.0 rich IR contract will update
655
+ this API contract before 1.0 release approval.
656
+
657
+ The `@jasonbelmonti/markdown-engine` package boundary remains limited to
658
+ parsing, normalization, deterministic validation, diagnostics, and
659
+ serialization. Profile compiler behavior, runtime lenses, MCP transport, agent
660
+ adapters, network services, persistence, LLM calls, semantic rubrics, and
661
+ arbitrary rule plugins are out of scope.