@agentshouse/mdmodel 0.1.0-alpha.9 → 0.1.0-c68e2039f1ca8

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 (320) hide show
  1. package/API.md +300 -0
  2. package/README.md +34 -189
  3. package/dist/blocks.d.ts +11 -0
  4. package/dist/blocks.d.ts.map +1 -0
  5. package/dist/blocks.js +62 -0
  6. package/dist/condition.d.ts +137 -0
  7. package/dist/condition.d.ts.map +1 -0
  8. package/dist/condition.js +394 -0
  9. package/dist/definition.d.ts +42 -27
  10. package/dist/definition.d.ts.map +1 -1
  11. package/dist/definition.js +473 -152
  12. package/dist/divide.d.ts +3 -0
  13. package/dist/divide.d.ts.map +1 -0
  14. package/dist/divide.js +310 -0
  15. package/dist/document.d.ts +2 -0
  16. package/dist/document.d.ts.map +1 -1
  17. package/dist/document.js +89 -120
  18. package/dist/fields.d.ts +4 -0
  19. package/dist/fields.d.ts.map +1 -0
  20. package/dist/fields.js +3 -0
  21. package/dist/index.d.ts +9 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +6 -3
  24. package/dist/links.d.ts +1 -0
  25. package/dist/links.d.ts.map +1 -1
  26. package/dist/links.js +618 -224
  27. package/dist/paths.d.ts +2 -3
  28. package/dist/paths.d.ts.map +1 -1
  29. package/dist/paths.js +27 -37
  30. package/dist/profile.js +1 -3
  31. package/dist/project.d.ts +19 -1
  32. package/dist/project.d.ts.map +1 -1
  33. package/dist/project.js +77 -94
  34. package/dist/refuse.d.ts.map +1 -1
  35. package/dist/refuse.js +2 -10
  36. package/dist/schema.d.ts +7 -5
  37. package/dist/schema.d.ts.map +1 -1
  38. package/dist/schema.js +156 -41
  39. package/dist/sections.d.ts +2 -1
  40. package/dist/sections.d.ts.map +1 -1
  41. package/dist/sections.js +44 -66
  42. package/dist/types.d.ts +30 -2
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/types.js +1 -3
  45. package/dist/validate.d.ts +3 -2
  46. package/dist/validate.d.ts.map +1 -1
  47. package/dist/validate.js +138 -141
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.d.ts.map +1 -1
  50. package/dist/version.js +1 -3
  51. package/dist/yaml.d.ts +2 -0
  52. package/dist/yaml.d.ts.map +1 -1
  53. package/dist/yaml.js +254 -190
  54. package/fixtures/README.md +85 -56
  55. package/fixtures/cases/absent-optional-field/library/companies/acme.md +4 -0
  56. package/fixtures/cases/absent-optional-field/library/deals/lead.md +6 -0
  57. package/fixtures/cases/absent-optional-field/schema.yaml +56 -0
  58. package/fixtures/cases/binary-any-reference/expected.json +1 -2
  59. package/fixtures/cases/binary-any-reference/schema.yaml +4 -0
  60. package/fixtures/cases/binary-document-collision/expected.json +1 -2
  61. package/fixtures/cases/binary-document-collision/schema.yaml +1 -0
  62. package/fixtures/cases/binary-link-outside-collection/expected.json +1 -2
  63. package/fixtures/cases/binary-link-outside-collection/schema.yaml +1 -0
  64. package/fixtures/cases/binary-prototype-reference/expected.json +1 -2
  65. package/fixtures/cases/binary-prototype-reference/schema.yaml +4 -0
  66. package/fixtures/cases/binary-typed-reference/expected.json +1 -2
  67. package/fixtures/cases/binary-typed-reference/schema.yaml +5 -0
  68. package/fixtures/cases/boolean-field/context.json +1 -0
  69. package/fixtures/cases/boolean-field/expected.json +6 -0
  70. package/fixtures/cases/boolean-field/library/companies/acme.md +4 -0
  71. package/fixtures/cases/boolean-field/library/deals/renewal.md +31 -0
  72. package/fixtures/cases/boolean-field/schema.yaml +56 -0
  73. package/fixtures/cases/bracketed-link-definition/schema.yaml +6 -0
  74. package/fixtures/cases/closed-nested-object/context.json +1 -0
  75. package/fixtures/cases/closed-nested-object/expected.json +6 -0
  76. package/fixtures/cases/closed-nested-object/library/companies/acme.md +4 -0
  77. package/fixtures/cases/closed-nested-object/library/deals/renewal.md +32 -0
  78. package/fixtures/cases/closed-nested-object/schema.yaml +56 -0
  79. package/fixtures/cases/composed-root-reference/context.json +1 -0
  80. package/fixtures/cases/composed-root-reference/expected.json +6 -0
  81. package/fixtures/cases/composed-root-reference/library/a.md +5 -0
  82. package/fixtures/cases/composed-root-reference/schema.yaml +13 -0
  83. package/fixtures/cases/date-time-format/context.json +1 -0
  84. package/fixtures/cases/date-time-format/expected.json +6 -0
  85. package/fixtures/cases/date-time-format/library/companies/acme.md +4 -0
  86. package/fixtures/cases/date-time-format/library/deals/renewal.md +31 -0
  87. package/fixtures/cases/date-time-format/schema.yaml +56 -0
  88. package/fixtures/cases/declared-binary-links/schema.yaml +1 -0
  89. package/fixtures/cases/default-unfilled/context.json +1 -0
  90. package/fixtures/cases/default-unfilled/expected.json +6 -0
  91. package/fixtures/cases/default-unfilled/library/companies/acme.md +4 -0
  92. package/fixtures/cases/default-unfilled/library/deals/renewal.md +29 -0
  93. package/fixtures/cases/default-unfilled/schema.yaml +56 -0
  94. package/fixtures/cases/defs-pattern/context.json +1 -0
  95. package/fixtures/cases/defs-pattern/expected.json +6 -0
  96. package/fixtures/cases/defs-pattern/library/companies/acme.md +4 -0
  97. package/fixtures/cases/defs-pattern/library/deals/renewal.md +31 -0
  98. package/fixtures/cases/defs-pattern/schema.yaml +56 -0
  99. package/fixtures/cases/dependent-rule/context.json +1 -0
  100. package/fixtures/cases/dependent-rule/expected.json +6 -0
  101. package/fixtures/cases/dependent-rule/library/companies/acme.md +4 -0
  102. package/fixtures/cases/dependent-rule/library/deals/renewal.md +30 -0
  103. package/fixtures/cases/dependent-rule/schema.yaml +56 -0
  104. package/fixtures/cases/duplicate-link-definition/schema.yaml +6 -0
  105. package/fixtures/cases/email-format/context.json +1 -0
  106. package/fixtures/cases/email-format/expected.json +6 -0
  107. package/fixtures/cases/email-format/library/companies/acme.md +4 -0
  108. package/fixtures/cases/email-format/library/deals/renewal.md +31 -0
  109. package/fixtures/cases/email-format/schema.yaml +56 -0
  110. package/fixtures/cases/fenced-link-definition/schema.yaml +6 -0
  111. package/fixtures/cases/field-enum/expected.json +2 -3
  112. package/fixtures/cases/field-enum/schema.yaml +7 -0
  113. package/fixtures/cases/frontmatter-schema/context.json +1 -0
  114. package/fixtures/cases/frontmatter-schema/expected.json +5 -0
  115. package/fixtures/cases/frontmatter-schema/schema.yaml +5 -0
  116. package/fixtures/cases/integer-field/context.json +1 -0
  117. package/fixtures/cases/integer-field/expected.json +6 -0
  118. package/fixtures/cases/integer-field/library/companies/acme.md +4 -0
  119. package/fixtures/cases/integer-field/library/deals/renewal.md +31 -0
  120. package/fixtures/cases/integer-field/schema.yaml +56 -0
  121. package/fixtures/cases/interrupted-link-definition/schema.yaml +1 -0
  122. package/fixtures/cases/json-schema-constructs/context.json +1 -0
  123. package/fixtures/cases/json-schema-constructs/expected.json +1 -0
  124. package/fixtures/cases/json-schema-constructs/library/companies/acme.md +4 -0
  125. package/fixtures/cases/json-schema-constructs/library/deals/renewal.md +31 -0
  126. package/fixtures/cases/json-schema-constructs/schema.yaml +56 -0
  127. package/fixtures/cases/list-of-objects-closed/context.json +1 -0
  128. package/fixtures/cases/list-of-objects-closed/expected.json +6 -0
  129. package/fixtures/cases/list-of-objects-closed/library/companies/acme.md +4 -0
  130. package/fixtures/cases/list-of-objects-closed/library/deals/renewal.md +32 -0
  131. package/fixtures/cases/list-of-objects-closed/schema.yaml +56 -0
  132. package/fixtures/cases/list-of-objects-item/context.json +1 -0
  133. package/fixtures/cases/list-of-objects-item/expected.json +6 -0
  134. package/fixtures/cases/list-of-objects-item/library/companies/acme.md +4 -0
  135. package/fixtures/cases/list-of-objects-item/library/deals/renewal.md +31 -0
  136. package/fixtures/cases/list-of-objects-item/schema.yaml +56 -0
  137. package/fixtures/cases/list-size/context.json +1 -0
  138. package/fixtures/cases/list-size/expected.json +6 -0
  139. package/fixtures/cases/list-size/library/companies/acme.md +4 -0
  140. package/fixtures/cases/list-size/library/deals/renewal.md +31 -0
  141. package/fixtures/cases/list-size/schema.yaml +56 -0
  142. package/fixtures/cases/long-fence-link-definition/schema.yaml +1 -0
  143. package/fixtures/cases/malformed-definition/expected.json +2 -4
  144. package/fixtures/cases/malformed-definition/schema.yaml +3 -0
  145. package/fixtures/cases/member-document/context.json +1 -0
  146. package/fixtures/cases/member-document/expected.json +1 -0
  147. package/fixtures/cases/member-document/library/people/ana.md +5 -0
  148. package/fixtures/cases/member-document/schema.yaml +5 -0
  149. package/fixtures/cases/missing-binary-image/expected.json +1 -2
  150. package/fixtures/cases/missing-binary-image/schema.yaml +1 -0
  151. package/fixtures/cases/missing-binary-link/expected.json +1 -2
  152. package/fixtures/cases/missing-binary-link/schema.yaml +1 -0
  153. package/fixtures/cases/missing-required-field/expected.json +2 -3
  154. package/fixtures/cases/missing-required-field/schema.yaml +7 -0
  155. package/fixtures/cases/missing-section-item-kind/expected.json +2 -4
  156. package/fixtures/cases/missing-section-item-kind/schema.yaml +6 -0
  157. package/fixtures/cases/misspelled-key/context.json +1 -0
  158. package/fixtures/cases/misspelled-key/expected.json +6 -0
  159. package/fixtures/cases/misspelled-key/library/companies/acme.md +4 -0
  160. package/fixtures/cases/misspelled-key/library/deals/renewal.md +32 -0
  161. package/fixtures/cases/misspelled-key/schema.yaml +56 -0
  162. package/fixtures/cases/nested-defs/context.json +1 -0
  163. package/fixtures/cases/nested-defs/expected.json +6 -0
  164. package/fixtures/cases/nested-defs/library/deal.md +6 -0
  165. package/fixtures/cases/nested-defs/schema.yaml +11 -0
  166. package/fixtures/cases/nested-required-field/context.json +1 -0
  167. package/fixtures/cases/nested-required-field/expected.json +6 -0
  168. package/fixtures/cases/nested-required-field/library/companies/acme.md +4 -0
  169. package/fixtures/cases/nested-required-field/library/deals/renewal.md +30 -0
  170. package/fixtures/cases/nested-required-field/schema.yaml +56 -0
  171. package/fixtures/cases/null-refused/context.json +1 -0
  172. package/fixtures/cases/null-refused/expected.json +6 -0
  173. package/fixtures/cases/null-refused/library/companies/acme.md +4 -0
  174. package/fixtures/cases/null-refused/library/deals/renewal.md +31 -0
  175. package/fixtures/cases/null-refused/schema.yaml +56 -0
  176. package/fixtures/cases/numeric-range/context.json +1 -0
  177. package/fixtures/cases/numeric-range/expected.json +6 -0
  178. package/fixtures/cases/numeric-range/library/companies/acme.md +4 -0
  179. package/fixtures/cases/numeric-range/library/deals/renewal.md +31 -0
  180. package/fixtures/cases/numeric-range/schema.yaml +56 -0
  181. package/fixtures/cases/one-of-variant/context.json +1 -0
  182. package/fixtures/cases/one-of-variant/expected.json +6 -0
  183. package/fixtures/cases/one-of-variant/library/companies/acme.md +4 -0
  184. package/fixtures/cases/one-of-variant/library/deals/renewal.md +31 -0
  185. package/fixtures/cases/one-of-variant/schema.yaml +56 -0
  186. package/fixtures/cases/open-map-value/context.json +1 -0
  187. package/fixtures/cases/open-map-value/expected.json +6 -0
  188. package/fixtures/cases/open-map-value/library/companies/acme.md +4 -0
  189. package/fixtures/cases/open-map-value/library/deals/renewal.md +31 -0
  190. package/fixtures/cases/open-map-value/schema.yaml +56 -0
  191. package/fixtures/cases/paragraph-link-definition/schema.yaml +1 -0
  192. package/fixtures/cases/prototype-field/expected.json +2 -3
  193. package/fixtures/cases/prototype-field/schema.yaml +6 -0
  194. package/fixtures/cases/quoted-link-definition/expected.json +1 -2
  195. package/fixtures/cases/quoted-link-definition/schema.yaml +1 -0
  196. package/fixtures/cases/referenced-missing-file/expected.json +0 -1
  197. package/fixtures/cases/referenced-missing-file/schema.yaml +11 -0
  198. package/fixtures/cases/remote-ref/context.json +1 -0
  199. package/fixtures/cases/remote-ref/expected.json +5 -0
  200. package/fixtures/cases/remote-ref/schema.yaml +3 -0
  201. package/fixtures/cases/second-member-document/context.json +1 -0
  202. package/fixtures/cases/second-member-document/expected.json +5 -0
  203. package/fixtures/cases/second-member-document/schema.yaml +4 -0
  204. package/fixtures/cases/section-item-enum/context.json +1 -0
  205. package/fixtures/cases/section-item-enum/expected.json +6 -0
  206. package/fixtures/cases/section-item-enum/library/task.md +8 -0
  207. package/fixtures/cases/section-item-enum/schema.yaml +3 -0
  208. package/fixtures/cases/section-item-kind/context.json +1 -0
  209. package/fixtures/cases/section-item-kind/expected.json +6 -0
  210. package/fixtures/cases/section-item-kind/library/task.md +8 -0
  211. package/fixtures/cases/section-item-kind/schema.yaml +3 -0
  212. package/fixtures/cases/section-items/context.json +1 -0
  213. package/fixtures/cases/section-items/expected.json +1 -0
  214. package/fixtures/cases/section-items/library/other.md +3 -0
  215. package/fixtures/cases/section-items/library/page.md +42 -0
  216. package/fixtures/cases/section-items/schema.yaml +10 -0
  217. package/fixtures/cases/section-reference-path/context.json +1 -0
  218. package/fixtures/cases/section-reference-path/expected.json +6 -0
  219. package/fixtures/cases/section-reference-path/library/other.md +3 -0
  220. package/fixtures/cases/section-reference-path/library/page.md +7 -0
  221. package/fixtures/cases/section-reference-path/schema.yaml +3 -0
  222. package/fixtures/cases/section-reference-type/context.json +1 -0
  223. package/fixtures/cases/section-reference-type/expected.json +6 -0
  224. package/fixtures/cases/section-reference-type/library/note.md +1 -0
  225. package/fixtures/cases/section-reference-type/library/page.md +7 -0
  226. package/fixtures/cases/section-reference-type/schema.yaml +3 -0
  227. package/fixtures/cases/shortcut-reference-link/context.json +1 -0
  228. package/fixtures/cases/shortcut-reference-link/expected.json +5 -0
  229. package/fixtures/cases/shortcut-reference-link/library/item.md +10 -0
  230. package/fixtures/cases/shortcut-reference-link/schema.yaml +6 -0
  231. package/fixtures/cases/string-length/context.json +1 -0
  232. package/fixtures/cases/string-length/expected.json +6 -0
  233. package/fixtures/cases/string-length/library/companies/acme.md +4 -0
  234. package/fixtures/cases/string-length/library/deals/renewal.md +31 -0
  235. package/fixtures/cases/string-length/schema.yaml +56 -0
  236. package/fixtures/cases/structured-section/expected.json +1 -2
  237. package/fixtures/cases/structured-section/schema.yaml +7 -0
  238. package/fixtures/cases/submission-keys/context.json +1 -0
  239. package/fixtures/cases/submission-keys/expected.json +1 -0
  240. package/fixtures/cases/submission-keys/library/notes/visitor.md +8 -0
  241. package/fixtures/cases/submission-keys/schema.yaml +4 -0
  242. package/fixtures/cases/text-field-kind/context.json +1 -0
  243. package/fixtures/cases/text-field-kind/expected.json +6 -0
  244. package/fixtures/cases/text-field-kind/library/page.md +4 -0
  245. package/fixtures/cases/text-field-kind/schema.yaml +4 -0
  246. package/fixtures/cases/truncated-link-definition/expected.json +1 -2
  247. package/fixtures/cases/truncated-link-definition/schema.yaml +1 -0
  248. package/fixtures/cases/types-folder-content/context.json +1 -0
  249. package/fixtures/cases/types-folder-content/expected.json +1 -0
  250. package/fixtures/cases/types-folder-content/library/types/task.md +4 -0
  251. package/fixtures/cases/types-folder-content/schema.yaml +4 -0
  252. package/fixtures/cases/unclosed-frontmatter/expected.json +1 -2
  253. package/fixtures/cases/undeclared-field/expected.json +2 -3
  254. package/fixtures/cases/undeclared-field/schema.yaml +6 -0
  255. package/fixtures/cases/unevaluated-items-closed/context.json +1 -0
  256. package/fixtures/cases/unevaluated-items-closed/expected.json +6 -0
  257. package/fixtures/cases/unevaluated-items-closed/library/trip.md +6 -0
  258. package/fixtures/cases/unevaluated-items-closed/schema.yaml +10 -0
  259. package/fixtures/cases/unknown-type/context.json +1 -0
  260. package/fixtures/cases/unknown-type/expected.json +6 -0
  261. package/fixtures/cases/unknown-type/library/item.md +3 -0
  262. package/fixtures/cases/unknown-type/schema.yaml +3 -0
  263. package/fixtures/cases/unlisted-reference-type/context.json +1 -0
  264. package/fixtures/cases/unlisted-reference-type/expected.json +5 -0
  265. package/fixtures/cases/unlisted-reference-type/schema.yaml +3 -0
  266. package/fixtures/cases/unresolved-link/expected.json +1 -2
  267. package/fixtures/cases/unresolved-link/schema.yaml +6 -0
  268. package/fixtures/cases/valid-typed-document/schema.yaml +16 -0
  269. package/fixtures/cases/wide-item-fence-link/schema.yaml +1 -0
  270. package/fixtures/division.json +409 -0
  271. package/fixtures/projection.json +90 -21
  272. package/package.json +3 -12
  273. package/dist/definition.js.map +0 -1
  274. package/dist/document.js.map +0 -1
  275. package/dist/index.js.map +0 -1
  276. package/dist/links.js.map +0 -1
  277. package/dist/paths.js.map +0 -1
  278. package/dist/profile.js.map +0 -1
  279. package/dist/project.js.map +0 -1
  280. package/dist/refuse.js.map +0 -1
  281. package/dist/schema.js.map +0 -1
  282. package/dist/sections.js.map +0 -1
  283. package/dist/types.js.map +0 -1
  284. package/dist/validate.js.map +0 -1
  285. package/dist/version.js.map +0 -1
  286. package/dist/yaml.js.map +0 -1
  287. package/fixtures/cases/binary-any-reference/library/types/note.md +0 -5
  288. package/fixtures/cases/binary-document-collision/library/types/note.md +0 -3
  289. package/fixtures/cases/binary-link-outside-collection/library/types/note.md +0 -3
  290. package/fixtures/cases/binary-prototype-reference/library/types/note.md +0 -5
  291. package/fixtures/cases/binary-typed-reference/library/types/note.md +0 -5
  292. package/fixtures/cases/binary-typed-reference/library/types/person.md +0 -3
  293. package/fixtures/cases/bracketed-link-definition/library/types/task.md +0 -7
  294. package/fixtures/cases/declared-binary-links/library/types/note.md +0 -3
  295. package/fixtures/cases/duplicate-link-definition/library/types/task.md +0 -7
  296. package/fixtures/cases/fenced-link-definition/library/types/task.md +0 -7
  297. package/fixtures/cases/field-enum/library/types/task.md +0 -8
  298. package/fixtures/cases/interrupted-link-definition/library/types/note.md +0 -3
  299. package/fixtures/cases/long-fence-link-definition/library/types/note.md +0 -3
  300. package/fixtures/cases/malformed-definition/library/types/task.md +0 -5
  301. package/fixtures/cases/missing-binary-image/library/types/note.md +0 -3
  302. package/fixtures/cases/missing-binary-link/library/types/note.md +0 -3
  303. package/fixtures/cases/missing-required-field/library/types/task.md +0 -8
  304. package/fixtures/cases/missing-section-item-kind/library/types/task.md +0 -7
  305. package/fixtures/cases/paragraph-link-definition/library/types/note.md +0 -3
  306. package/fixtures/cases/prd-definition/library/types/task.md +0 -16
  307. package/fixtures/cases/prototype-field/library/types/task.md +0 -7
  308. package/fixtures/cases/quoted-link-definition/library/types/note.md +0 -3
  309. package/fixtures/cases/referenced-missing-file/library/types/person.md +0 -5
  310. package/fixtures/cases/referenced-missing-file/library/types/task.md +0 -8
  311. package/fixtures/cases/structured-section/library/types/task.md +0 -8
  312. package/fixtures/cases/truncated-link-definition/library/types/note.md +0 -3
  313. package/fixtures/cases/undeclared-field/library/types/task.md +0 -7
  314. package/fixtures/cases/unresolved-link/library/types/task.md +0 -7
  315. package/fixtures/cases/valid-typed-document/library/types/person.md +0 -5
  316. package/fixtures/cases/valid-typed-document/library/types/task.md +0 -13
  317. package/fixtures/cases/wide-item-fence-link/library/types/note.md +0 -3
  318. package/markdown-syntax-profile.md +0 -9
  319. /package/fixtures/cases/{prd-definition → absent-optional-field}/context.json +0 -0
  320. /package/fixtures/cases/{prd-definition → absent-optional-field}/expected.json +0 -0
@@ -2,10 +2,13 @@
2
2
 
3
3
  Each directory under `cases/` is one text Library, one validation context,
4
4
  and one expected verdict. Consumers load the files under `library/` as
5
- path-to-content, load the required `context.json` as `ValidationContext`, and
6
- compare `validate(library, context)` against `expected.json`. Text-only cases
7
- carry `{ "binaryTargets": [] }`. Binary targets are membership declarations;
8
- the corpus supplies no binary bytes or placeholder Documents.
5
+ path-to-content, an absent `library/` being the empty Library, and the
6
+ required `context.json` as `ValidationContext`. Cases with a `schema.yaml` call
7
+ `verify` with its text, then compare its refusal or
8
+ `validate(library, context, verified.model)` against `expected.json`. Other
9
+ cases compare `validate(library, context)` directly. Text-only cases carry
10
+ `{ "binaryTargets": [] }`. Binary targets are membership declarations; the
11
+ corpus supplies no binary bytes or placeholder Documents.
9
12
 
10
13
  ## Preamble operations
11
14
 
@@ -15,29 +18,52 @@ the validation Libraries. Each row carries `name`, exact `source`, expected
15
18
  the source, compare `Document.preamble`, apply
16
19
  `{ kind: 'preamble', prior: preamble, value }`, and compare the serialized result.
17
20
  These cases cover empty and heading-free Documents, frontmatter boundaries,
18
- fenced headings, newline/source preservation, and the Runtime moved-export
19
- example from its [approved candidate](https://github.com/agentshouse/mdruntime/blob/ed0a6f0b95317d9ade846ec1f2871a3997519bc4/test/rooms.ts).
20
- The package check runs every row in ESM, CommonJS, and the browser-target VM
21
- bundle, also checking stale priors, immutable Documents, and conversion of the
22
- Runtime link rewrite into a preamble operation through public APIs.
21
+ fenced headings, newline/source preservation, and a moved-export link rewrite.
23
22
 
24
23
  ## Projection
25
24
 
26
25
  `projection.json` exercises the public `project` seam separately from the
27
- validation Libraries. It carries `definitions`, the MD Type definition sources
28
- keyed by a label, and `cases`. Each case carries `name`, the `file` the Document
29
- sits at, exact `source`, the `documentTargets` the collection holds as Markdown
30
- documents, the `definition` label or `null`, an optional `move`, and `expected`.
26
+ validation Libraries. It carries `schema`, one schema text, and `cases`. Each
27
+ case carries `name`, the `file` the Document sits at, exact `source`, the
28
+ `documentTargets` the collection holds as Markdown documents, the `definition`
29
+ type name or `null`, an optional `move`, an optional `links` inventory, an
30
+ optional `relabel`, an optional `filtered`, and `expected`.
31
31
  A case carrying a `move` lists both the Path its target sits at and the Path the
32
32
  move takes it to, because the collection holds that target either way.
33
33
 
34
- Parse the source, call `project(document, file, context)` with
35
- `{ documentTargets, definition }`, and compare against `expected`, replacing the
36
- `<package-version>` token in a refusal the same way `cases/` does. Then check
34
+ Verify the schema, parse the source, call `project(document, file, context)`
35
+ with `documentTargets` and the verified type `definition` names, and compare against `expected`. Where
36
+ `links` is present, compare `links(document, file)` against it. Then check
37
37
  that the source is unchanged. Where `move` is present, apply
38
38
  `{ kind: 'link', href, to }`, confirm the serialized Document changed, and
39
39
  confirm the projection of the moved Document is still exactly `expected`: a
40
- moved Path rewrites destinations and leaves the projection alone.
40
+ moved Path rewrites destinations and leaves the projection alone. Where
41
+ `relabel` is present, split the source at its `from`, which the source carries
42
+ exactly once, join the pieces with its `to`, parse the result, and confirm its
43
+ projection is exactly the relabel's own `expected` and differs from the case's:
44
+ a changed visible label changes the projection. Where `filtered` is present,
45
+ apply `{ kind: 'field', name: field, value, prior }` with its `field` and
46
+ `value` and the Document's current value as `prior`, confirm the serialized
47
+ Document changed, and confirm its projection is still exactly `expected`: a
48
+ change to a value that is not prose leaves the projection alone.
49
+
50
+ ## Division
51
+
52
+ `division.json` exercises the public `divide` seam separately from the
53
+ validation Libraries. It carries `schema` and `cases` like `projection.json`,
54
+ and each case adds the `budget` the division runs under. Parse the source, call
55
+ `divide(document, file, context)` with `{ documentTargets, definition, budget }`,
56
+ and compare against `expected`: either `{ "ok": true, "spans": [...] }`, where
57
+ each span carries the exact `text` to embed and the exact `source` the returned
58
+ `start` and `end` address in that case's source, or `{ "ok": false, "refusal": ... }`. Then
59
+ check that the source is unchanged. A span's `text` and `source` differ where
60
+ the embedded text repeats a continuation's own heading, carries an internal
61
+ link's visible label, or carries a frontmatter value without the quotes the
62
+ author wrote, and where the addressed extent encloses a declared structured
63
+ section between two pieces one span packs together or the whole of a free-text
64
+ value one span carries part of. `start` and `end` are UTF-8 byte offsets into the case's
65
+ source; every source in this corpus is ASCII, so a consumer compares the
66
+ addressed `source` with an ordinary string slice.
41
67
 
42
68
  ## Verdict format
43
69
 
@@ -53,54 +79,57 @@ Refusal:
53
79
  {
54
80
  "verdict": "refuse",
55
81
  "file": "<library path>",
56
- "location": "<field or section name>",
82
+ "location": "<JSON Pointer or section name>",
57
83
  "rule": "<rule id>",
58
- "version": "<package version>",
59
84
  "referrers": ["<library path>"]
60
85
  }
61
86
  ```
62
87
 
63
- `location` is omitted when the rule is not about a field or section.
64
- `referrers` is present only for `referenced-missing-file`.
65
- The `version` field in these fixtures is the token `<package-version>`;
66
- replace it with the package's semantic version before comparing.
88
+ `location` is the JSON Pointer of the frontmatter value a rule concerns, such
89
+ as `/shipping/city`, or the name of the section it concerns, and is omitted when
90
+ the rule concerns neither. `referrers` is present only for
91
+ `referenced-missing-file`. A schema refusal carries no `file`; its `location`
92
+ is the JSON Pointer of the schema part it concerns, such as
93
+ `/task/properties/title/type`, or `line <n>` for `schema-yaml`.
67
94
 
68
95
  ## Rule ids
69
96
 
70
- | Rule | When |
71
- | ------------------------- | ------------------------------------------------------------------------ |
72
- | `binary-target-document` | A path is declared as both a text Document and a binary target |
73
- | `frontmatter-fence` | A present frontmatter fence does not parse as the accepted YAML subset |
74
- | `definition-type` | A `types/` file is not a definition, or its type name is empty |
75
- | `definition-shape` | A definition carries a key other than `type`, `fields`, and `sections` |
76
- | `definition-fields` | `fields` is not a mapping |
77
- | `definition-field` | A field spec is not an object of the accepted keys |
78
- | `definition-field-kind` | A field kind is not text, string, number, date, enum, reference, or list |
79
- | `definition-enum-values` | An enum field has no non-empty string `values` |
80
- | `definition-reference-to` | A reference field has no non-empty `to` type name |
81
- | `definition-list-of` | A list field or list section has no `of` that is one scalar kind |
82
- | `definition-sections` | `sections` is not a mapping |
83
- | `definition-section` | A section spec is not an object of the accepted keys |
84
- | `definition-section-kind` | A section kind is not `text` or `list` |
85
- | `definition-duplicate` | Two definition files share a type name |
86
- | `unknown-type` | A file declares a type that is not in `types/` |
87
- | `required-field` | A required field is missing |
88
- | `required-section` | A required section heading is missing |
89
- | `field-kind` | A field value fails its kind, including date |
90
- | `field-enum` | A field value is not one of the declared enum values |
91
- | `undeclared-field` | A typed file carries a frontmatter field its type does not declare |
92
- | `unresolved-reference` | A reference does not resolve to a current file of the required type |
93
- | `unresolved-link` | A relative body link does not resolve to a current path |
94
- | `structured-section` | A declared structured-list section does not parse as a list |
95
- | `referenced-missing-file` | Remaining files still reference an absent path |
96
- | `unwritable-link` | No Markdown destination form can carry a new link destination |
97
-
98
- `unwritable-link` is refused by `apply`, not by `validate`, so no case under
99
- `cases/` carries it.
97
+ A frontmatter value that fails a JSON Schema keyword is refused under that
98
+ keyword's name, such as `required`, `type`, `enum`, `format`, `pattern`,
99
+ `minimum`, `maxLength`, `maxItems` or `oneOf`, at the value's JSON Pointer; a
100
+ missing required key is named by the Pointer it would sit at. A malformed
101
+ schema keyword is refused under that keyword's name at its Pointer in the
102
+ schema text. The model's own rules:
103
+
104
+ | Rule | When |
105
+ | ------------------------- | --------------------------------------------------------------------------------- |
106
+ | `binary-target-document` | A path is declared as both a text Document and a binary target |
107
+ | `frontmatter-fence` | A present frontmatter fence does not parse as the accepted YAML subset |
108
+ | `schema-yaml` | A schema text is not a mapping in the accepted YAML subset |
109
+ | `schema-type-name` | A type name is empty, starts with `$`, or is `any` or `attachment` |
110
+ | `schema` | A type or subschema is neither a mapping nor a boolean |
111
+ | `schema-keyword` | A schema carries a key that is no JSON Schema 2020-12 keyword the model admits |
112
+ | `$defs` | `$defs` is not a mapping |
113
+ | `$ref` | A `$ref` reaches no subschema under `#/$defs/` of the same file |
114
+ | `reference` | `reference` is not on a string or names something but a type, `any`, `attachment` |
115
+ | `sections` | A section is not `{ kind: text }` or `{ kind: list, items }` over one scalar |
116
+ | `member_document` | The mark is not a boolean, or a second type carries it |
117
+ | `unknown-type` | A file declares a type absent from the governing schema |
118
+ | `undeclared-field` | A closed object carries a key it does not declare |
119
+ | `required-section` | A required section heading is missing |
120
+ | `unresolved-reference` | A reference field, list item or section item reaches no file of a type it names |
121
+ | `unresolved-link` | A relative body link does not resolve to a current path |
122
+ | `structured-section` | A declared list section is not a list of values of its item schema |
123
+ | `referenced-missing-file` | Remaining files still reference an absent path |
124
+ | `unwritable-link` | No Markdown destination form can carry a new link destination |
125
+ | `division-budget` | A division budget is not a whole number of at least 2 |
126
+
127
+ `unwritable-link` is refused by `apply` and `division-budget` by `divide`, not
128
+ by `validate`, so no case under `cases/` carries either.
100
129
 
101
130
  ## YAML subset
102
131
 
103
- Frontmatter is a mapping in a closed YAML subset: block and flow mappings,
104
- block and flow sequences, plain and quoted scalars, `true`, `false`, `null`,
132
+ Frontmatter and a schema text are each a mapping in a closed YAML subset: block and flow mappings,
133
+ block and flow sequences, a block sequence item that opens a mapping, plain and quoted scalars, `true`, `false`, `null`,
105
134
  and decimal numbers. It does not take YAML 1.1 boolean or timestamp aliases,
106
- octal numbers, anchors, tags, or block scalars.
135
+ octal numbers, anchors, tags, or block scalars. A mapping carries each key once.
@@ -0,0 +1,4 @@
1
+ ---
2
+ type: company
3
+ name: Acme
4
+ ---
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: deal
3
+ title: Lead
4
+ company: ../companies/acme.md
5
+ stage: lead
6
+ ---
@@ -0,0 +1,56 @@
1
+ $defs:
2
+ address:
3
+ type: object
4
+ properties:
5
+ city: { type: string }
6
+ country: { type: string, pattern: '^[A-Z]{2}$' }
7
+ required: [city]
8
+ company:
9
+ properties:
10
+ name: { type: string }
11
+ deal:
12
+ type: object
13
+ properties:
14
+ title: { type: string, minLength: 1, maxLength: 40 }
15
+ company: { type: string, reference: company }
16
+ stage: { enum: [lead, proposal, won, lost], default: lead }
17
+ amount: { type: integer, minimum: 0 }
18
+ probability: { type: number, minimum: 0, maximum: 1 }
19
+ open: { type: boolean }
20
+ closes_at: { type: string, format: date-time }
21
+ contact_email: { type: string, format: email }
22
+ note: { type: [string, 'null'] }
23
+ shipping: { $ref: '#/$defs/address' }
24
+ tags: { type: array, items: { type: string }, maxItems: 3 }
25
+ lines:
26
+ type: array
27
+ items:
28
+ type: object
29
+ properties:
30
+ product: { type: string }
31
+ quantity: { type: integer, minimum: 1 }
32
+ required: [product, quantity]
33
+ blocks:
34
+ type: array
35
+ items:
36
+ oneOf:
37
+ - properties:
38
+ kind: { const: paragraph }
39
+ text: { type: string }
40
+ required: [kind, text]
41
+ - properties:
42
+ kind: { const: quiz }
43
+ question: { type: string }
44
+ required: [kind, question]
45
+ attributes:
46
+ type: object
47
+ additionalProperties: { type: string }
48
+ required: [title, company, stage]
49
+ if:
50
+ properties:
51
+ stage: { const: won }
52
+ required: [stage]
53
+ then:
54
+ required: [amount]
55
+ sections:
56
+ Description: { kind: text }
@@ -2,6 +2,5 @@
2
2
  "verdict": "refuse",
3
3
  "file": "notes/draft.md",
4
4
  "rule": "unresolved-reference",
5
- "version": "<package-version>",
6
- "location": "subject"
5
+ "location": "/subject"
7
6
  }
@@ -0,0 +1,4 @@
1
+ note:
2
+ properties:
3
+ subject: { type: string, reference: any }
4
+ required: [subject]
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "verdict": "refuse",
3
3
  "file": "notes/draft.md",
4
- "rule": "binary-target-document",
5
- "version": "<package-version>"
4
+ "rule": "binary-target-document"
6
5
  }
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "verdict": "refuse",
3
3
  "file": "notes/draft.md",
4
- "rule": "unresolved-link",
5
- "version": "<package-version>"
4
+ "rule": "unresolved-link"
6
5
  }
@@ -2,6 +2,5 @@
2
2
  "verdict": "refuse",
3
3
  "file": "notes/draft.md",
4
4
  "rule": "unresolved-reference",
5
- "version": "<package-version>",
6
- "location": "subject"
5
+ "location": "/subject"
7
6
  }
@@ -0,0 +1,4 @@
1
+ note:
2
+ properties:
3
+ subject: { type: string, reference: any }
4
+ required: [subject]
@@ -2,6 +2,5 @@
2
2
  "verdict": "refuse",
3
3
  "file": "notes/draft.md",
4
4
  "rule": "unresolved-reference",
5
- "version": "<package-version>",
6
- "location": "subject"
5
+ "location": "/subject"
7
6
  }
@@ -0,0 +1,5 @@
1
+ note:
2
+ properties:
3
+ subject: { type: string, reference: person }
4
+ required: [subject]
5
+ person: {}
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "deals/renewal.md",
4
+ "location": "/open",
5
+ "rule": "type"
6
+ }
@@ -0,0 +1,4 @@
1
+ ---
2
+ type: company
3
+ name: Acme
4
+ ---
@@ -0,0 +1,31 @@
1
+ ---
2
+ type: deal
3
+ title: Renewal
4
+ company: ../companies/acme.md
5
+ stage: won
6
+ amount: 1200
7
+ probability: 0.5
8
+ open: "no"
9
+ closes_at: 2026-10-10T09:30:00Z
10
+ contact_email: ana@example.com
11
+ note: null
12
+ shipping:
13
+ city: Paris
14
+ country: FR
15
+ tags: [renewal, priority]
16
+ lines:
17
+ - product: seats
18
+ quantity: 10
19
+ - product: support
20
+ quantity: 1
21
+ blocks:
22
+ - kind: paragraph
23
+ text: Signed.
24
+ - { kind: quiz, question: Renew next year? }
25
+ attributes:
26
+ region: emea
27
+ ---
28
+
29
+ ## Description
30
+
31
+ The yearly renewal.
@@ -0,0 +1,56 @@
1
+ $defs:
2
+ address:
3
+ type: object
4
+ properties:
5
+ city: { type: string }
6
+ country: { type: string, pattern: '^[A-Z]{2}$' }
7
+ required: [city]
8
+ company:
9
+ properties:
10
+ name: { type: string }
11
+ deal:
12
+ type: object
13
+ properties:
14
+ title: { type: string, minLength: 1, maxLength: 40 }
15
+ company: { type: string, reference: company }
16
+ stage: { enum: [lead, proposal, won, lost], default: lead }
17
+ amount: { type: integer, minimum: 0 }
18
+ probability: { type: number, minimum: 0, maximum: 1 }
19
+ open: { type: boolean }
20
+ closes_at: { type: string, format: date-time }
21
+ contact_email: { type: string, format: email }
22
+ note: { type: [string, 'null'] }
23
+ shipping: { $ref: '#/$defs/address' }
24
+ tags: { type: array, items: { type: string }, maxItems: 3 }
25
+ lines:
26
+ type: array
27
+ items:
28
+ type: object
29
+ properties:
30
+ product: { type: string }
31
+ quantity: { type: integer, minimum: 1 }
32
+ required: [product, quantity]
33
+ blocks:
34
+ type: array
35
+ items:
36
+ oneOf:
37
+ - properties:
38
+ kind: { const: paragraph }
39
+ text: { type: string }
40
+ required: [kind, text]
41
+ - properties:
42
+ kind: { const: quiz }
43
+ question: { type: string }
44
+ required: [kind, question]
45
+ attributes:
46
+ type: object
47
+ additionalProperties: { type: string }
48
+ required: [title, company, stage]
49
+ if:
50
+ properties:
51
+ stage: { const: won }
52
+ required: [stage]
53
+ then:
54
+ required: [amount]
55
+ sections:
56
+ Description: { kind: text }
@@ -0,0 +1,6 @@
1
+ task:
2
+ properties:
3
+ title: { type: string }
4
+ required: [title]
5
+ sections:
6
+ Description: { kind: text, required: true }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "deals/renewal.md",
4
+ "location": "/shipping/cty",
5
+ "rule": "undeclared-field"
6
+ }
@@ -0,0 +1,4 @@
1
+ ---
2
+ type: company
3
+ name: Acme
4
+ ---
@@ -0,0 +1,32 @@
1
+ ---
2
+ type: deal
3
+ title: Renewal
4
+ company: ../companies/acme.md
5
+ stage: won
6
+ amount: 1200
7
+ probability: 0.5
8
+ open: false
9
+ closes_at: 2026-10-10T09:30:00Z
10
+ contact_email: ana@example.com
11
+ note: null
12
+ shipping:
13
+ city: Paris
14
+ country: FR
15
+ cty: Lyon
16
+ tags: [renewal, priority]
17
+ lines:
18
+ - product: seats
19
+ quantity: 10
20
+ - product: support
21
+ quantity: 1
22
+ blocks:
23
+ - kind: paragraph
24
+ text: Signed.
25
+ - { kind: quiz, question: Renew next year? }
26
+ attributes:
27
+ region: emea
28
+ ---
29
+
30
+ ## Description
31
+
32
+ The yearly renewal.
@@ -0,0 +1,56 @@
1
+ $defs:
2
+ address:
3
+ type: object
4
+ properties:
5
+ city: { type: string }
6
+ country: { type: string, pattern: '^[A-Z]{2}$' }
7
+ required: [city]
8
+ company:
9
+ properties:
10
+ name: { type: string }
11
+ deal:
12
+ type: object
13
+ properties:
14
+ title: { type: string, minLength: 1, maxLength: 40 }
15
+ company: { type: string, reference: company }
16
+ stage: { enum: [lead, proposal, won, lost], default: lead }
17
+ amount: { type: integer, minimum: 0 }
18
+ probability: { type: number, minimum: 0, maximum: 1 }
19
+ open: { type: boolean }
20
+ closes_at: { type: string, format: date-time }
21
+ contact_email: { type: string, format: email }
22
+ note: { type: [string, 'null'] }
23
+ shipping: { $ref: '#/$defs/address' }
24
+ tags: { type: array, items: { type: string }, maxItems: 3 }
25
+ lines:
26
+ type: array
27
+ items:
28
+ type: object
29
+ properties:
30
+ product: { type: string }
31
+ quantity: { type: integer, minimum: 1 }
32
+ required: [product, quantity]
33
+ blocks:
34
+ type: array
35
+ items:
36
+ oneOf:
37
+ - properties:
38
+ kind: { const: paragraph }
39
+ text: { type: string }
40
+ required: [kind, text]
41
+ - properties:
42
+ kind: { const: quiz }
43
+ question: { type: string }
44
+ required: [kind, question]
45
+ attributes:
46
+ type: object
47
+ additionalProperties: { type: string }
48
+ required: [title, company, stage]
49
+ if:
50
+ properties:
51
+ stage: { const: won }
52
+ required: [stage]
53
+ then:
54
+ required: [amount]
55
+ sections:
56
+ Description: { kind: text }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "people/ada.md",
4
+ "rule": "referenced-missing-file",
5
+ "referrers": ["a.md"]
6
+ }
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: task
3
+ title: A
4
+ owner: people/ada.md
5
+ ---
@@ -0,0 +1,13 @@
1
+ $defs:
2
+ assigned:
3
+ properties:
4
+ owner: { type: string, reference: person }
5
+ required: [owner]
6
+ person:
7
+ properties:
8
+ name: { type: string }
9
+ task:
10
+ allOf:
11
+ - $ref: '#/$defs/assigned'
12
+ properties:
13
+ title: { type: string }
@@ -0,0 +1 @@
1
+ { "binaryTargets": [] }
@@ -0,0 +1,6 @@
1
+ {
2
+ "verdict": "refuse",
3
+ "file": "deals/renewal.md",
4
+ "location": "/closes_at",
5
+ "rule": "format"
6
+ }
@@ -0,0 +1,4 @@
1
+ ---
2
+ type: company
3
+ name: Acme
4
+ ---
@@ -0,0 +1,31 @@
1
+ ---
2
+ type: deal
3
+ title: Renewal
4
+ company: ../companies/acme.md
5
+ stage: won
6
+ amount: 1200
7
+ probability: 0.5
8
+ open: false
9
+ closes_at: 2026-10-10
10
+ contact_email: ana@example.com
11
+ note: null
12
+ shipping:
13
+ city: Paris
14
+ country: FR
15
+ tags: [renewal, priority]
16
+ lines:
17
+ - product: seats
18
+ quantity: 10
19
+ - product: support
20
+ quantity: 1
21
+ blocks:
22
+ - kind: paragraph
23
+ text: Signed.
24
+ - { kind: quiz, question: Renew next year? }
25
+ attributes:
26
+ region: emea
27
+ ---
28
+
29
+ ## Description
30
+
31
+ The yearly renewal.