explico 0.11.1 → 0.13.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 (298) hide show
  1. package/CHANGELOG.md +1292 -1
  2. package/README.md +91 -13
  3. package/dist/compiler/astro.d.ts +11 -4
  4. package/dist/compiler/astro.js +48 -15
  5. package/dist/compiler/cli.d.ts +27 -12
  6. package/dist/compiler/cli.js +96 -25
  7. package/dist/compiler/collections.d.ts +33 -2
  8. package/dist/compiler/collections.js +91 -19
  9. package/dist/compiler/content-schema.d.ts +31 -0
  10. package/dist/compiler/content-schema.js +82 -0
  11. package/dist/compiler/content-schemas.d.ts +422 -0
  12. package/dist/compiler/content-schemas.js +229 -0
  13. package/dist/compiler/course-config.d.ts +1 -1
  14. package/dist/compiler/course-config.js +30 -2
  15. package/dist/compiler/exports/combined-html.d.ts +97 -0
  16. package/dist/compiler/exports/combined-html.js +492 -0
  17. package/dist/compiler/exports/download-index.d.ts +9 -0
  18. package/dist/compiler/exports/download-index.js +33 -0
  19. package/dist/compiler/exports/export-chapters.d.ts +98 -0
  20. package/dist/compiler/exports/export-chapters.js +164 -0
  21. package/dist/compiler/exports/export-pages.d.ts +48 -0
  22. package/dist/compiler/exports/export-pages.js +22 -0
  23. package/dist/compiler/exports/html-tree.d.ts +22 -0
  24. package/dist/compiler/exports/html-tree.js +47 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +106 -0
  26. package/dist/compiler/exports/latex/capture.js +435 -0
  27. package/dist/compiler/exports/latex/compile.d.ts +73 -0
  28. package/dist/compiler/exports/latex/compile.js +115 -0
  29. package/dist/compiler/exports/latex/document.d.ts +102 -0
  30. package/dist/compiler/exports/latex/document.js +349 -0
  31. package/dist/compiler/exports/latex/explico.cls +415 -0
  32. package/dist/compiler/exports/latex/numbering.d.ts +18 -0
  33. package/dist/compiler/exports/latex/numbering.js +57 -0
  34. package/dist/compiler/exports/latex/serialize.d.ts +112 -0
  35. package/dist/compiler/exports/latex/serialize.js +606 -0
  36. package/dist/compiler/exports/latex-export.d.ts +44 -0
  37. package/dist/compiler/exports/latex-export.js +169 -0
  38. package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
  39. package/dist/compiler/exports/markdown/serialize.js +527 -0
  40. package/dist/compiler/exports/markdown-export.d.ts +39 -0
  41. package/dist/compiler/exports/markdown-export.js +240 -0
  42. package/dist/compiler/exports/standalone-html.d.ts +15 -5
  43. package/dist/compiler/exports/standalone-html.js +23 -51
  44. package/dist/compiler/exports/static-tree.d.ts +253 -0
  45. package/dist/compiler/exports/static-tree.js +625 -0
  46. package/dist/compiler/exports/zip.d.ts +16 -0
  47. package/dist/compiler/exports/zip.js +72 -0
  48. package/dist/compiler/lesson-derivation.d.ts +8 -2
  49. package/dist/compiler/lesson-derivation.js +6 -7
  50. package/dist/compiler/manifest.d.ts +115 -8
  51. package/dist/compiler/manifest.js +221 -39
  52. package/dist/compiler/render-diagnostics.d.ts +65 -0
  53. package/dist/compiler/render-diagnostics.js +56 -0
  54. package/dist/compiler/resolution-report.d.ts +3 -1
  55. package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
  56. package/dist/curriculum/validation.d.ts +9 -3
  57. package/dist/curriculum/validation.js +35 -17
  58. package/dist/reference/base-abbreviations.d.mts +2 -2
  59. package/dist/reference/base-abbreviations.mjs +2 -2
  60. package/dist/reference/base-operators.d.mts +8 -2
  61. package/dist/reference/base-operators.mjs +8 -10
  62. package/dist/reference/base-roman-labels.d.mts +4 -3
  63. package/dist/reference/base-roman-labels.mjs +4 -3
  64. package/dist/reference/citation-format.d.mts +35 -0
  65. package/dist/reference/citation-format.mjs +74 -0
  66. package/dist/reference/consistency.d.ts +5 -9
  67. package/dist/reference/consistency.js +34 -38
  68. package/dist/reference/curriculum-alignment.js +3 -24
  69. package/dist/reference/derived-entries.d.ts +45 -0
  70. package/dist/reference/derived-entries.js +50 -0
  71. package/dist/reference/derived-notation.d.mts +125 -0
  72. package/dist/reference/derived-notation.mjs +121 -0
  73. package/dist/reference/diagram-fit.d.ts +45 -0
  74. package/dist/reference/diagram-fit.js +54 -0
  75. package/dist/reference/diagram-layout.d.ts +59 -10
  76. package/dist/reference/diagram-layout.js +85 -24
  77. package/dist/reference/diagrams-validate.d.ts +1 -1
  78. package/dist/reference/equations-validate.d.ts +1 -1
  79. package/dist/reference/equations-validate.js +26 -1
  80. package/dist/reference/equations.d.mts +27 -0
  81. package/dist/reference/equations.mjs +120 -22
  82. package/dist/reference/examples-validate.d.ts +1 -1
  83. package/dist/reference/figure-fit.d.ts +70 -0
  84. package/dist/reference/figure-fit.js +60 -0
  85. package/dist/reference/figure-pictures.d.ts +27 -0
  86. package/dist/reference/figure-pictures.js +49 -0
  87. package/dist/reference/figure-symbols.d.ts +32 -0
  88. package/dist/reference/figure-symbols.js +55 -0
  89. package/dist/reference/figures-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-abbreviations.js +1 -1
  92. package/dist/reference/gate-course-rules.d.ts +4 -3
  93. package/dist/reference/gate-course-rules.js +9 -4
  94. package/dist/reference/gate-heading-length.d.ts +2 -0
  95. package/dist/reference/gate-heading-length.js +7 -5
  96. package/dist/reference/gate-heading-structure.js +26 -0
  97. package/dist/reference/gate-indices.d.ts +35 -0
  98. package/dist/reference/gate-indices.js +299 -0
  99. package/dist/reference/gate-math.d.ts +32 -2
  100. package/dist/reference/gate-math.js +68 -12
  101. package/dist/reference/gate-mdx-expression.d.ts +17 -0
  102. package/dist/reference/gate-mdx-expression.js +29 -0
  103. package/dist/reference/gate-non-chapter-pages.d.ts +39 -0
  104. package/dist/reference/gate-non-chapter-pages.js +156 -0
  105. package/dist/reference/gate-part-pages.d.ts +4 -2
  106. package/dist/reference/gate-part-pages.js +2 -1
  107. package/dist/reference/gate-prop-text.d.ts +28 -0
  108. package/dist/reference/gate-prop-text.js +106 -0
  109. package/dist/reference/gate-prose-notation.d.ts +3 -1
  110. package/dist/reference/gate-prose-notation.js +4 -2
  111. package/dist/reference/gate-retired-keys.d.ts +35 -0
  112. package/dist/reference/gate-retired-keys.js +58 -0
  113. package/dist/reference/gloss.d.ts +2 -0
  114. package/dist/reference/katex-measure.d.ts +17 -0
  115. package/dist/reference/katex-measure.js +223 -0
  116. package/dist/reference/keyed-float-validate.js +12 -0
  117. package/dist/reference/keyed-jsx-floats.d.mts +2 -2
  118. package/dist/reference/keyed-jsx-floats.mjs +11 -14
  119. package/dist/reference/lab-katex-validate.d.ts +43 -0
  120. package/dist/reference/lab-katex-validate.js +96 -0
  121. package/dist/reference/lab-math-scope.d.ts +1 -0
  122. package/dist/reference/lab-math-scope.js +13 -2
  123. package/dist/reference/lint-ignore.js +1 -1
  124. package/dist/reference/math-glyphs.d.mts +94 -2
  125. package/dist/reference/math-glyphs.mjs +851 -46
  126. package/dist/reference/mixed-content.d.ts +8 -3
  127. package/dist/reference/mixed-content.js +4 -2
  128. package/dist/reference/page-notation.d.ts +39 -7
  129. package/dist/reference/page-notation.js +62 -10
  130. package/dist/reference/raw-floats.mjs +37 -13
  131. package/dist/reference/references.d.ts +11 -1
  132. package/dist/reference/references.js +6 -0
  133. package/dist/reference/registry.js +115 -11
  134. package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
  135. package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
  136. package/dist/reference/rehype-heading-anchors.mjs +11 -1
  137. package/dist/reference/remark-citation.mjs +0 -0
  138. package/dist/reference/remark-notation.mjs +275 -23
  139. package/dist/reference/render-lab-math.d.ts +13 -2
  140. package/dist/reference/render-lab-math.js +18 -6
  141. package/dist/reference/sections-validate.d.ts +28 -3
  142. package/dist/reference/sections-validate.js +31 -4
  143. package/dist/reference/sections.d.mts +24 -6
  144. package/dist/reference/sections.mjs +92 -17
  145. package/dist/reference/tables-validate.d.ts +1 -1
  146. package/dist/reference/tables-validate.js +24 -1
  147. package/dist/reference/text-measure.d.ts +10 -0
  148. package/dist/reference/text-measure.js +41 -0
  149. package/dist/reference/text-widths.d.ts +10 -0
  150. package/dist/reference/text-widths.js +1238 -0
  151. package/dist/reference/types.d.ts +64 -8
  152. package/dist/rules/catalog.js +223 -32
  153. package/dist/rules/in-effect.d.ts +1 -1
  154. package/dist/rules/rules.schema.json +100 -27
  155. package/dist/rules/settings.d.ts +1 -1
  156. package/dist/rules/settings.js +7 -0
  157. package/dist/session/PreferenceStore.d.ts +18 -1
  158. package/dist/session/PreferenceStore.js +29 -1
  159. package/docs/authoring/README.md +33 -21
  160. package/docs/authoring/agents.md +38 -7
  161. package/docs/authoring/components.md +115 -10
  162. package/docs/authoring/migrating.md +878 -0
  163. package/docs/authoring/notation.md +285 -30
  164. package/docs/authoring/retired.md +1 -0
  165. package/docs/authoring/structure-rules.md +84 -45
  166. package/docs/authoring/structure.md +67 -2
  167. package/docs/authoring/visualizations.md +167 -25
  168. package/package.json +12 -7
  169. package/src/compiler/astro.ts +72 -21
  170. package/src/compiler/cli.ts +143 -35
  171. package/src/compiler/collections.ts +158 -27
  172. package/src/compiler/content-schema.ts +108 -0
  173. package/src/compiler/content-schemas.ts +391 -0
  174. package/src/compiler/course-config.ts +86 -15
  175. package/src/compiler/exports/combined-html.ts +669 -0
  176. package/src/compiler/exports/download-index.ts +36 -0
  177. package/src/compiler/exports/export-chapters.ts +233 -0
  178. package/src/compiler/exports/export-pages.ts +44 -0
  179. package/src/compiler/exports/html-tree.ts +62 -0
  180. package/src/compiler/exports/latex/capture.ts +651 -0
  181. package/src/compiler/exports/latex/compile.ts +168 -0
  182. package/src/compiler/exports/latex/document.ts +479 -0
  183. package/src/compiler/exports/latex/explico.cls +415 -0
  184. package/src/compiler/exports/latex/numbering.ts +74 -0
  185. package/src/compiler/exports/latex/serialize.ts +787 -0
  186. package/src/compiler/exports/latex/texlive-packages.txt +76 -0
  187. package/src/compiler/exports/latex-export.ts +255 -0
  188. package/src/compiler/exports/markdown/serialize.ts +632 -0
  189. package/src/compiler/exports/markdown-export.ts +329 -0
  190. package/src/compiler/exports/standalone-html.ts +39 -70
  191. package/src/compiler/exports/static-tree.ts +944 -0
  192. package/src/compiler/exports/zip.ts +88 -0
  193. package/src/compiler/lesson-derivation.ts +13 -8
  194. package/src/compiler/manifest.ts +387 -40
  195. package/src/compiler/render-diagnostics.ts +111 -0
  196. package/src/components/CourseContents.astro +6 -0
  197. package/src/components/HoverPanel.astro +204 -6
  198. package/src/components/PartOverview.astro +18 -9
  199. package/src/components/abbreviation/AbbreviationGlossary.astro +6 -2
  200. package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
  201. package/src/components/assessments/AssessmentRunner.tsx +7 -1
  202. package/src/components/assessments/AssessmentSet.astro +14 -1
  203. package/src/components/block-heading.ts +38 -0
  204. package/src/components/card-arrow.ts +18 -4
  205. package/src/components/card-carry.ts +406 -0
  206. package/src/components/card-drag.ts +1 -30
  207. package/src/components/card-fetch.ts +87 -0
  208. package/src/components/card-manager.ts +1222 -38
  209. package/src/components/card-memory.ts +5 -1
  210. package/src/components/card-placement.ts +211 -13
  211. package/src/components/card-side.ts +281 -31
  212. package/src/components/citation/CitationLayer.astro +45 -40
  213. package/src/components/diagram/Diagram.astro +123 -44
  214. package/src/components/disclosure/print-as-shown.ts +42 -6
  215. package/src/components/figure/Figure.astro +17 -1
  216. package/src/components/figure/FloatCaption.astro +4 -17
  217. package/src/components/figure/figure-state.ts +26 -17
  218. package/src/components/float-toolbar.ts +274 -0
  219. package/src/components/memo/Memo.astro +21 -10
  220. package/src/components/memo/ShortNote.astro +4 -0
  221. package/src/components/notation/NotationGlossary.astro +22 -17
  222. package/src/components/notation/NotationLayer.astro +300 -79
  223. package/src/components/notation/NotationMath.astro +8 -1
  224. package/src/components/notation/render-notation-math.ts +51 -5
  225. package/src/components/readable-text.ts +25 -0
  226. package/src/components/reference/FloatRefLayer.astro +162 -89
  227. package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
  228. package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
  229. package/src/components/starlight/LayoutPageSidebar.astro +17 -3
  230. package/src/components/starlight/LayoutSocialIcons.astro +79 -29
  231. package/src/components/starlight/LessonExportActions.astro +384 -310
  232. package/src/components/starlight/LessonFooter.astro +66 -8
  233. package/src/components/starlight/PrintController.astro +28 -0
  234. package/src/components/starlight/TextSizeControl.astro +137 -0
  235. package/src/components/starlight/print-controller.ts +380 -0
  236. package/src/components/starlight/section-summary.ts +13 -5
  237. package/src/components/table/Table.astro +14 -22
  238. package/src/components/table/table-truncation.ts +15 -0
  239. package/src/content-config.ts +20 -312
  240. package/src/curriculum/validation.ts +72 -24
  241. package/src/reference/base-abbreviations.mjs +2 -2
  242. package/src/reference/base-operators.mjs +8 -10
  243. package/src/reference/base-roman-labels.mjs +4 -3
  244. package/src/reference/citation-format.mjs +74 -0
  245. package/src/reference/consistency.ts +47 -61
  246. package/src/reference/curriculum-alignment.ts +4 -41
  247. package/src/reference/derived-entries.ts +68 -0
  248. package/src/reference/derived-notation.mjs +121 -0
  249. package/src/reference/diagram-fit.ts +104 -0
  250. package/src/reference/diagram-layout.ts +182 -51
  251. package/src/reference/diagrams-validate.ts +2 -1
  252. package/src/reference/equations-validate.ts +41 -2
  253. package/src/reference/equations.mjs +120 -22
  254. package/src/reference/examples-validate.ts +1 -0
  255. package/src/reference/figure-fit.ts +139 -0
  256. package/src/reference/figure-pictures.ts +96 -0
  257. package/src/reference/figure-symbols.ts +117 -0
  258. package/src/reference/figures-validate.ts +2 -1
  259. package/src/reference/gate-abbreviations.ts +1 -1
  260. package/src/reference/gate-course-rules.ts +22 -7
  261. package/src/reference/gate-heading-length.ts +25 -7
  262. package/src/reference/gate-heading-structure.ts +40 -1
  263. package/src/reference/gate-indices.ts +470 -0
  264. package/src/reference/gate-math.ts +102 -13
  265. package/src/reference/gate-mdx-expression.ts +45 -0
  266. package/src/reference/gate-non-chapter-pages.ts +241 -0
  267. package/src/reference/gate-part-pages.ts +1 -1
  268. package/src/reference/gate-prop-text.ts +145 -0
  269. package/src/reference/gate-prose-notation.ts +19 -5
  270. package/src/reference/gate-retired-keys.ts +101 -0
  271. package/src/reference/gloss.ts +2 -0
  272. package/src/reference/katex-measure.ts +384 -0
  273. package/src/reference/keyed-float-validate.ts +22 -0
  274. package/src/reference/keyed-jsx-floats.mjs +11 -14
  275. package/src/reference/lab-katex-validate.ts +156 -0
  276. package/src/reference/lab-math-scope.ts +15 -1
  277. package/src/reference/lint-ignore.ts +4 -1
  278. package/src/reference/math-glyphs.mjs +851 -46
  279. package/src/reference/mixed-content.ts +11 -2
  280. package/src/reference/page-notation.ts +114 -14
  281. package/src/reference/raw-floats.mjs +37 -13
  282. package/src/reference/references.ts +14 -0
  283. package/src/reference/registry.ts +170 -9
  284. package/src/reference/rehype-fail-katex-errors.mjs +19 -0
  285. package/src/reference/rehype-heading-anchors.mjs +11 -1
  286. package/src/reference/remark-citation.mjs +0 -0
  287. package/src/reference/remark-notation.mjs +275 -23
  288. package/src/reference/render-lab-math.ts +40 -5
  289. package/src/reference/sections-validate.ts +67 -3
  290. package/src/reference/sections.mjs +92 -17
  291. package/src/reference/tables-validate.ts +40 -2
  292. package/src/reference/text-measure.ts +63 -0
  293. package/src/reference/text-widths.ts +133 -0
  294. package/src/reference/types.ts +75 -8
  295. package/src/rules/catalog.ts +241 -31
  296. package/src/rules/settings.ts +10 -0
  297. package/src/session/PreferenceStore.ts +53 -0
  298. package/src/styles/global.css +322 -83
@@ -138,12 +138,6 @@ interface CoursePart {
138
138
  * `##` heading inside a lesson. Removed in the next MINOR release.
139
139
  */
140
140
  type CourseSection = CoursePart;
141
- interface CourseConventions {
142
- /** Notation key a lesson's stated sign convention must resolve to. */
143
- readonly signConventionKey: string;
144
- /** Competency id that also counts as declaring the cash-flow perspective. */
145
- readonly cashFlowPerspectiveCompetency: string;
146
- }
147
141
  /**
148
142
  * A course's edits to the shipped named-operator baseline (`BASE_OPERATORS`),
149
143
  * consumed by the `operator-explained` consistency check (rule N8). An operator
@@ -367,6 +361,27 @@ type BlockLabels = 'icon' | 'icon-word';
367
361
  declare const BLOCK_LABELS_DEFAULT: BlockLabels;
368
362
  /** Validate `CourseConfig.blockLabels` and apply its default. Throws on a bad value. */
369
363
  declare function normalizeBlockLabels(config: CourseConfig): BlockLabels;
364
+ /**
365
+ * How a course's explanation cards start for a reader who has not chosen
366
+ * yet (`cards` in the course settings). Every field is optional;
367
+ * {@link normalizeCards} fills in the defaults.
368
+ */
369
+ interface CourseCards {
370
+ /**
371
+ * Whether "Carry cards" starts on (rule K1, `card-carry.ts`): a kept card
372
+ * stays on screen as the page scrolls and comes along to every page of the
373
+ * course. A reader's own choice always wins. Default `false`.
374
+ */
375
+ readonly carry?: boolean;
376
+ }
377
+ /** {@link CourseCards} with every default applied. */
378
+ interface NormalizedCards {
379
+ readonly carry: boolean;
380
+ }
381
+ /** The engine's defaults: a reader turns carrying on (D8). */
382
+ declare const CARDS_DEFAULTS: NormalizedCards;
383
+ /** Validate `CourseConfig.cards` and apply its defaults. Throws on a bad value. */
384
+ declare function normalizeCards(config: CourseConfig): NormalizedCards;
370
385
  /**
371
386
  * Rule S2's word ranges (`rules.S2.bridgeMinWords` … in the course settings),
372
387
  * in the shape `gate-part-pages.ts` takes. Built by `partPageLimits` in
@@ -382,15 +397,33 @@ interface NormalizedPartPages {
382
397
  readonly maxWords: number;
383
398
  };
384
399
  }
385
- /** Optional, build-time lesson artifacts exposed to readers. */
400
+ /**
401
+ * Optional, build-time downloads and print actions: each chapter, each part and
402
+ * the course download; every page prints.
403
+ */
386
404
  interface LessonExportsConfig {
387
- /** Emit one self-contained, offline-capable HTML file per lesson. */
405
+ /** Emit one self-contained, offline-capable HTML file per page. */
388
406
  readonly standaloneHtml?: boolean;
389
- /** Offer the browser's native print dialog with explico's lesson print layout. */
407
+ /** Offer the browser's native print dialog with explico's print layout. */
390
408
  readonly print?: boolean;
409
+ /**
410
+ * Emit each chapter's LaTeX source — a zipped folder with explico's class,
411
+ * the chapter and its figures — and the PDF compiled from that folder.
412
+ */
413
+ readonly latex?: boolean;
414
+ /**
415
+ * Paper size of the LaTeX documents: `letter` (default, LaTeX's own) or
416
+ * `a4`. A plain `string` because the settings arrive as JSON.
417
+ */
418
+ readonly latexPaper?: string;
419
+ /**
420
+ * Emit each chapter, each part and the course as Markdown — GitHub's
421
+ * flavour with `$…$` math, its figures as PNG — zipped with its assets.
422
+ */
423
+ readonly markdown?: boolean;
391
424
  /**
392
425
  * Published course root used for links that intentionally leave the exported
393
- * lesson. Required at export time when such a link is present.
426
+ * page. Required at export time when such a link is present.
394
427
  */
395
428
  readonly canonicalBaseUrl?: string;
396
429
  }
@@ -398,6 +431,9 @@ interface LessonExportsConfig {
398
431
  interface NormalizedLessonExports {
399
432
  readonly standaloneHtml: boolean;
400
433
  readonly print: boolean;
434
+ readonly latex: boolean;
435
+ readonly latexPaper: 'letter' | 'a4';
436
+ readonly markdown: boolean;
401
437
  readonly canonicalBaseUrl?: string;
402
438
  }
403
439
  interface CourseConfig {
@@ -415,7 +451,12 @@ interface CourseConfig {
415
451
  readonly sections?: readonly CoursePart[];
416
452
  /** Namespaces used by competency / notation `domain` fields. */
417
453
  readonly domains: readonly string[];
418
- readonly conventions: CourseConventions;
454
+ /**
455
+ * @deprecated No longer read: rule S5, the only reader, was retired. A
456
+ * course that still sets it gets a `config-deprecated-key` warning; delete
457
+ * the block. Removed from the type in the next MINOR release.
458
+ */
459
+ readonly conventions?: unknown;
419
460
  /** Reader-facing identity + knowledge version (the `[?]` About panel). */
420
461
  readonly about: CourseAbout;
421
462
  /**
@@ -438,12 +479,14 @@ interface CourseConfig {
438
479
  readonly romanLabels?: CourseRomanLabels;
439
480
  /** @deprecated Moved to `rules.N14` (same shape). Honoured for now; removed in a later MINOR. */
440
481
  readonly abbreviations?: CourseAbbreviations;
441
- /** Per-lesson export actions and downloadable artifacts. Disabled when omitted. */
482
+ /** Per-page export actions and downloadable artifacts. Disabled when omitted. */
442
483
  readonly lessonExports?: LessonExportsConfig;
443
484
  /** Every collapse default: headings, checks, memos, floats, tables. */
444
485
  readonly disclosure?: CourseDisclosure;
445
486
  /** Outline and float numbering. The defaults apply when omitted. */
446
487
  readonly numbering?: CourseNumbering;
488
+ /** How explanation cards start for a reader: "Carry cards" on or off. */
489
+ readonly cards?: CourseCards;
447
490
  /**
448
491
  * How a check's and an example set's heading names its kind: `icon-word`
449
492
  * (default) or `icon` ({@link BlockLabels}). A plain `string` because the
@@ -461,4 +504,4 @@ declare function normalizeLessonExports(config: CourseConfig): NormalizedLessonE
461
504
  */
462
505
  declare function coursePartsOf(config: CourseConfig): readonly CoursePart[];
463
506
 
464
- export { type CourseSection as A, type BlockLabels as B, type CourseConfig as C, DISCLOSURE_DEFAULTS as D, coursePartsOf as E, normalizeBlockLabels as F, normalizeDisclosure as G, normalizeLessonExports as H, normalizeNumbering as I, type LessonExportsConfig as L, type ManifestRules as M, type NormalizedPartPages as N, type ResolvedRules as R, type CourseAbbreviations as a, type NormalizedLessonExports as b, type NormalizedDisclosure as c, type NormalizedNumbering as d, type ConfigDiagnostic as e, type CourseConventions as f, type CourseOperators as g, type CourseRomanLabels as h, type CourseAccents as i, type CourseRulesConfig as j, type RuleSettingsInput as k, catalogIds as l, disabledCodes as m, dropDisabled as n, rulesJsonSchema as o, partPageLimits as p, rulesSnapshot as q, resolveRules as r, BLOCK_LABELS_DEFAULT as s, type CourseAbout as t, type CourseAuthor as u, type CourseDisclosure as v, type CourseHeadingDisclosure as w, type CourseNumbering as x, type CourseOpenDefault as y, type CoursePart as z };
507
+ export { type CourseOpenDefault as A, type BlockLabels as B, type CourseConfig as C, type CoursePart as D, type CourseSection as E, DISCLOSURE_DEFAULTS as F, coursePartsOf as G, normalizeBlockLabels as H, normalizeCards as I, normalizeDisclosure as J, normalizeLessonExports as K, type LessonExportsConfig as L, type ManifestRules as M, type NormalizedLessonExports as N, normalizeNumbering as O, type ResolvedRules as R, type NormalizedDisclosure as a, type NormalizedNumbering as b, type NormalizedCards as c, type ConfigDiagnostic as d, type CourseOperators as e, type CourseRomanLabels as f, type CourseAccents as g, type CourseAbbreviations as h, type NormalizedPartPages as i, type CourseRulesConfig as j, type RuleSettingsInput as k, catalogIds as l, disabledCodes as m, dropDisabled as n, rulesJsonSchema as o, partPageLimits as p, rulesSnapshot as q, resolveRules as r, BLOCK_LABELS_DEFAULT as s, CARDS_DEFAULTS as t, type CourseAbout as u, type CourseAuthor as v, type CourseCards as w, type CourseDisclosure as x, type CourseHeadingDisclosure as y, type CourseNumbering as z };
@@ -10,7 +10,6 @@ interface CompetencyDefinition {
10
10
  }
11
11
  interface LessonDefinition {
12
12
  readonly id: string;
13
- readonly status: 'draft' | 'in-review' | 'reviewed';
14
13
  readonly requires: readonly string[];
15
14
  readonly teaches: readonly string[];
16
15
  readonly assessments: readonly string[];
@@ -23,6 +22,8 @@ interface AssessmentItemBase {
23
22
  readonly id: string;
24
23
  readonly competencyId: string;
25
24
  readonly evidenceKind: 'direct' | 'transfer';
25
+ readonly prompt?: string;
26
+ readonly explanation?: string;
26
27
  }
27
28
  interface NumericAssessmentItem extends AssessmentItemBase {
28
29
  readonly type: 'numeric';
@@ -45,7 +46,6 @@ interface AssessmentDefinition {
45
46
  }
46
47
  interface SourceDefinition {
47
48
  readonly id: string;
48
- readonly editorialStatus: 'draft' | 'in-review' | 'reviewed';
49
49
  }
50
50
  interface TrackDefinition {
51
51
  readonly id: string;
@@ -59,8 +59,14 @@ interface CurriculumCatalog {
59
59
  readonly tracks: readonly TrackDefinition[];
60
60
  }
61
61
  interface CurriculumIssue {
62
- readonly kind: 'invalid-id' | 'duplicate-id' | 'unknown-reference' | 'duplicate-reference' | 'self-prerequisite' | 'cycle' | 'lesson-order' | 'track-order' | 'assessment-coverage' | 'invalid-assessment' | 'review-state' | 'teaches-support';
62
+ readonly kind: 'invalid-id' | 'duplicate-id' | 'unknown-reference' | 'duplicate-reference' | 'self-prerequisite' | 'cycle' | 'lesson-order' | 'track-order' | 'assessment-coverage' | 'invalid-assessment' | 'missing-assumptions' | 'teaches-support' | 'retired-key' | 'content-schema' | 'part-empty' | 'lesson-outside-parts' | 'unsupported-key' | 'ignored-key' | 'markup-in-prop' | 'citation-in-link';
63
63
  readonly message: string;
64
+ /**
65
+ * The content file the issue is about, when it is about one file rather
66
+ * than the graph (`retired-key`, `content-schema`), and the 1-based line in it.
67
+ */
68
+ readonly file?: string;
69
+ readonly line?: number;
64
70
  /**
65
71
  * `error` (default) blocks the build; `warning` is reported but non-blocking.
66
72
  * New checks enter as warnings and are promoted once they run clean on real
@@ -1,3 +1,5 @@
1
+ import { citationsInLinks, citedIds, withoutLinkCitations } from "../reference/citation-format.mjs";
2
+ const CHECK_TEXT_REFERENCE = /\[\[[^\]\n]*\]\]|\[@[^\]\n]*\]/;
1
3
  const VALID_ID = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
2
4
  function buildIndex(kind, entries, issues) {
3
5
  const index = /* @__PURE__ */ new Map();
@@ -23,13 +25,7 @@ function buildIndex(kind, entries, issues) {
23
25
  function citedSourceIds(body) {
24
26
  if (!body) return /* @__PURE__ */ new Set();
25
27
  const withoutCode = body.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "");
26
- const ids = /* @__PURE__ */ new Set();
27
- for (const match of withoutCode.matchAll(
28
- /\[@([a-z0-9][a-z0-9.-]*)(?:;[^\]]*)?\]/g
29
- )) {
30
- ids.add((match[1] ?? "").trim());
31
- }
32
- return ids;
28
+ return citedIds(withoutLinkCitations(withoutCode));
33
29
  }
34
30
  function findDuplicates(values) {
35
31
  const seen = /* @__PURE__ */ new Set();
@@ -173,18 +169,22 @@ function validateCurriculum(catalog) {
173
169
  }
174
170
  }
175
171
  const cited = citedSourceIds(lesson.body);
172
+ if (lesson.body !== void 0) {
173
+ const blank = (code) => code.replace(/[^\n]/g, " ");
174
+ const prose = lesson.body.replace(/```[\s\S]*?```/g, blank).replace(/`[^`\n]*`/g, blank);
175
+ for (const { text } of citationsInLinks(prose)) {
176
+ issues.push({
177
+ kind: "citation-in-link",
178
+ message: `${lesson.id} puts the citation ${text} inside a link's text, where it prints as typed: a link cannot hold the citation's own link. Cite after the link: [the words](https://\u2026) ${text}.`
179
+ });
180
+ }
181
+ }
176
182
  for (const sourceId of lesson.sources) {
177
- const source = sources.get(sourceId);
178
- if (!source) {
183
+ if (!sources.has(sourceId)) {
179
184
  issues.push({
180
185
  kind: "unknown-reference",
181
186
  message: `${lesson.id} references unknown source ${sourceId}`
182
187
  });
183
- } else if (lesson.status === "reviewed" && source.editorialStatus !== "reviewed") {
184
- issues.push({
185
- kind: "review-state",
186
- message: `${lesson.id} is reviewed but source ${sourceId} is ${source.editorialStatus}`
187
- });
188
188
  }
189
189
  if (lesson.body !== void 0 && !cited.has(sourceId)) {
190
190
  issues.push({
@@ -203,7 +203,7 @@ function validateCurriculum(catalog) {
203
203
  }
204
204
  if (lesson.teaches.length > 0 && lesson.assumptions.length === 0) {
205
205
  issues.push({
206
- kind: "review-state",
206
+ kind: "missing-assumptions",
207
207
  message: `${lesson.id} teaches quantitative content but declares no assumptions`
208
208
  });
209
209
  }
@@ -232,10 +232,28 @@ function validateCurriculum(catalog) {
232
232
  message: `${assessment.id}/${item.id} has unknown correct option ${item.correctOptionId}`
233
233
  });
234
234
  }
235
- } else if (!Number.isFinite(item.answer.value) || !Number.isFinite(item.answer.tolerance) || item.answer.tolerance <= 0) {
235
+ } else if (!Number.isFinite(item.answer.value)) {
236
+ issues.push({
237
+ kind: "invalid-assessment",
238
+ message: `${assessment.id}/${item.id} answer.value must be a number`
239
+ });
240
+ } else if (!Number.isFinite(item.answer.tolerance) || item.answer.tolerance <= 0) {
241
+ issues.push({
242
+ kind: "invalid-assessment",
243
+ message: `${assessment.id}/${item.id} answer.tolerance must be a number greater than 0, not ${String(item.answer.tolerance)}: an answer within it of answer.value is correct. For an exact whole-number answer, use a tolerance below one half, such as 0.4.`
244
+ });
245
+ }
246
+ const texts = [
247
+ ["prompt", item.prompt],
248
+ ["explanation", item.explanation],
249
+ ...item.type === "single-choice" ? item.options.map((option) => [`option ${option.id}`, option.label]) : []
250
+ ];
251
+ for (const [field, text] of texts) {
252
+ const found = text === void 0 ? null : CHECK_TEXT_REFERENCE.exec(text);
253
+ if (!found) continue;
236
254
  issues.push({
237
255
  kind: "invalid-assessment",
238
- message: `${assessment.id}/${item.id} has an invalid numeric answer or tolerance`
256
+ message: `${assessment.id}/${item.id} ${field} contains ${found[0]}, which a knowledge check shows exactly as written: its text renders math, not references or citations. Name the equation, section or source in words, or cite it in the lesson text the check follows.`
239
257
  });
240
258
  }
241
259
  const evidence = evidenceByCompetency.get(item.competencyId) ?? {
@@ -24,7 +24,7 @@ export function isBaseAbbreviation(token: any, abbreviations: any): boolean;
24
24
  * `content/abbreviations/*.md` table, not here. This baseline only holds
25
25
  * abbreviations so common in ordinary English/academic writing that spelling
26
26
  * them out on every course would be noise, not help. A course adds a real
27
- * one via `abbreviations.allow` in `course.config.ts`, the same shape as
28
- * `operators.allow` / `romanLabels.allow`.
27
+ * one via `rules.N14.allow` in its course config, the same shape as
28
+ * `rules.N8.allow` / `rules.N10.allow`.
29
29
  */
30
30
  export const BASE_ABBREVIATIONS: Readonly<Set<string>>;
@@ -11,8 +11,8 @@
11
11
  * `content/abbreviations/*.md` table, not here. This baseline only holds
12
12
  * abbreviations so common in ordinary English/academic writing that spelling
13
13
  * them out on every course would be noise, not help. A course adds a real
14
- * one via `abbreviations.allow` in `course.config.ts`, the same shape as
15
- * `operators.allow` / `romanLabels.allow`.
14
+ * one via `rules.N14.allow` in its course config, the same shape as
15
+ * `rules.N8.allow` / `rules.N10.allow`.
16
16
  */
17
17
  export const BASE_ABBREVIATIONS = Object.freeze(
18
18
  new Set([
@@ -26,10 +26,16 @@ export function isBaseOperator(name: any, operators: any): boolean;
26
26
  * baseline — trig, the exponential and logarithms, limits, extrema, and the
27
27
  * big operators — kept greppable and small like the base library.
28
28
  *
29
+ * Only the plain limit, `\lim`, is baseline. `\limsup` / `\liminf` and the
30
+ * category-theory `\injlim` / `\projlim` / `\varlimsup` / `\varliminf` /
31
+ * `\varinjlim` / `\varprojlim` read fine to someone who already knows them,
32
+ * but none is "everyone knows this" the way `\lim` or `\max` is — a course
33
+ * that uses one owes it a notation entry, like any other domain operator.
34
+ *
29
35
  * A domain operator (`Var`, `Cov`, `Corr`, `sgn`, `erf`, `Tr`, `argmax`, …) is
30
36
  * written `\operatorname{…}` and is NOT here: it earns a card, a
31
- * `notation.local` entry, or an explicit line in `course.config.ts`
32
- * (`operators.allow`).
37
+ * `notation.local` entry, or an explicit line in the course config
38
+ * (`rules.N8.allow`).
33
39
  *
34
40
  * Entries are the KaTeX command without its backslash (`exp`, not `\exp`) for
35
41
  * `op` nodes, and the verbatim body for `\operatorname{…}` nodes (`Var`).
@@ -13,10 +13,16 @@
13
13
  * baseline — trig, the exponential and logarithms, limits, extrema, and the
14
14
  * big operators — kept greppable and small like the base library.
15
15
  *
16
+ * Only the plain limit, `\lim`, is baseline. `\limsup` / `\liminf` and the
17
+ * category-theory `\injlim` / `\projlim` / `\varlimsup` / `\varliminf` /
18
+ * `\varinjlim` / `\varprojlim` read fine to someone who already knows them,
19
+ * but none is "everyone knows this" the way `\lim` or `\max` is — a course
20
+ * that uses one owes it a notation entry, like any other domain operator.
21
+ *
16
22
  * A domain operator (`Var`, `Cov`, `Corr`, `sgn`, `erf`, `Tr`, `argmax`, …) is
17
23
  * written `\operatorname{…}` and is NOT here: it earns a card, a
18
- * `notation.local` entry, or an explicit line in `course.config.ts`
19
- * (`operators.allow`).
24
+ * `notation.local` entry, or an explicit line in the course config
25
+ * (`rules.N8.allow`).
20
26
  *
21
27
  * Entries are the KaTeX command without its backslash (`exp`, not `\exp`) for
22
28
  * `op` nodes, and the verbatim body for `\operatorname{…}` nodes (`Var`).
@@ -47,14 +53,6 @@ export const BASE_OPERATORS = Object.freeze(
47
53
  'coth',
48
54
  // limits
49
55
  'lim',
50
- 'limsup',
51
- 'liminf',
52
- 'injlim',
53
- 'projlim',
54
- 'varlimsup',
55
- 'varliminf',
56
- 'varinjlim',
57
- 'varprojlim',
58
56
  // extrema
59
57
  'max',
60
58
  'min',
@@ -28,8 +28,9 @@ export function isBaseRomanLabel(name: any, romanLabels: any): boolean;
28
28
  * always a course-specific named quantity (`KE`, `PV`, `Var`-as-a-label) that
29
29
  * genuinely benefits from a hover card, so the shipped baseline holds only
30
30
  * the qualifiers that function like an operator's extremum subscript
31
- * (`x_{\mathrm{opt}}`) rather than naming a quantity of their own. A course
32
- * adds a real one via `romanLabels.allow` in `course.config.ts`, the same
33
- * shape as `operators.allow`.
31
+ * (`x_{\mathrm{opt}}`) rather than naming a quantity of their own. That
32
+ * clears rule N10 only: the indexed symbol still needs a card (rule N23).
33
+ * A course adds a real one via `rules.N10.allow` in its course config, the
34
+ * same shape as `rules.N8.allow`.
34
35
  */
35
36
  export const BASE_ROMAN_LABELS: Readonly<Set<string>>;
@@ -15,9 +15,10 @@
15
15
  * always a course-specific named quantity (`KE`, `PV`, `Var`-as-a-label) that
16
16
  * genuinely benefits from a hover card, so the shipped baseline holds only
17
17
  * the qualifiers that function like an operator's extremum subscript
18
- * (`x_{\mathrm{opt}}`) rather than naming a quantity of their own. A course
19
- * adds a real one via `romanLabels.allow` in `course.config.ts`, the same
20
- * shape as `operators.allow`.
18
+ * (`x_{\mathrm{opt}}`) rather than naming a quantity of their own. That
19
+ * clears rule N10 only: the indexed symbol still needs a card (rule N23).
20
+ * A course adds a real one via `rules.N10.allow` in its course config, the
21
+ * same shape as `rules.N8.allow`.
21
22
  */
22
23
  export const BASE_ROMAN_LABELS = Object.freeze(new Set(['opt']));
23
24
 
@@ -44,6 +44,35 @@ export function referenceLabelFromText(referenceText: string, locator: string |
44
44
  * @returns {string}
45
45
  */
46
46
  export function formatSourceLabel(record: Record<string, unknown>): string;
47
+ /**
48
+ * The citations one bracket holds, in order, as `{ id, locator }`. A
49
+ * `;`-separated part that is only `@id` starts another citation (Pandoc's
50
+ * `[@a; @b]`); any other part belongs to the locator of the citation before
51
+ * it, so a locator may itself contain `;` (`[@a; §1.2; eq. 3]`).
52
+ *
53
+ * @param {string} firstId
54
+ * @param {string | undefined} rest
55
+ * @returns {{ id: string, locator: string }[]}
56
+ */
57
+ export function bracketCitations(firstId: string, rest: string | undefined): {
58
+ id: string;
59
+ locator: string;
60
+ }[];
61
+ /** Every source id a text cites, `[@a; @b]` included. */
62
+ export function citedIds(text: any): Set<any>;
63
+ /**
64
+ * Each citation written inside a link's text, as `{ index, text }`: the
65
+ * offset of its `[@` in `text`, and the citation as written.
66
+ */
67
+ export function citationsInLinks(text: any): {
68
+ index: number;
69
+ text: string;
70
+ }[];
71
+ /**
72
+ * `text` with every citation inside a link's text blanked, offsets kept: what
73
+ * the page cites, for a scan that counts a lesson's sources.
74
+ */
75
+ export function withoutLinkCitations(text: any): string;
47
76
  /**
48
77
  * Pure helpers for turning a source record plus a per-citation locator into
49
78
  * the strings shown in the inline panel and the end-of-page reference list.
@@ -53,3 +82,9 @@ export function formatSourceLabel(record: Record<string, unknown>): string;
53
82
  * file system, or Astro APIs.
54
83
  */
55
84
  export const SOURCE_ID_PATTERN: RegExp;
85
+ /**
86
+ * One `[@…]` bracket: `[@id]`, `[@id; locator]`, or several citations as
87
+ * Pandoc writes them, `[@a; @b; p. 7]`. Group 1 is the first id, group 2 the
88
+ * rest after the first `;`, read by {@link bracketCitations}.
89
+ */
90
+ export const CITATION_BRACKET: RegExp;
@@ -131,3 +131,77 @@ export function formatSourceLabel(record) {
131
131
  .filter(Boolean)
132
132
  .join(', ');
133
133
  }
134
+
135
+ /**
136
+ * One `[@…]` bracket: `[@id]`, `[@id; locator]`, or several citations as
137
+ * Pandoc writes them, `[@a; @b; p. 7]`. Group 1 is the first id, group 2 the
138
+ * rest after the first `;`, read by {@link bracketCitations}.
139
+ */
140
+ export const CITATION_BRACKET = /\[@([a-z0-9][a-z0-9.-]*)(?:;[ \t\r\n]*([^\]]*))?\]/g;
141
+
142
+ /**
143
+ * The citations one bracket holds, in order, as `{ id, locator }`. A
144
+ * `;`-separated part that is only `@id` starts another citation (Pandoc's
145
+ * `[@a; @b]`); any other part belongs to the locator of the citation before
146
+ * it, so a locator may itself contain `;` (`[@a; §1.2; eq. 3]`).
147
+ *
148
+ * @param {string} firstId
149
+ * @param {string | undefined} rest
150
+ * @returns {{ id: string, locator: string }[]}
151
+ */
152
+ export function bracketCitations(firstId, rest) {
153
+ const citations = [{ id: String(firstId).trim(), parts: [] }];
154
+ for (const part of String(rest ?? '').split(';')) {
155
+ const next = /^\s*@([a-z0-9][a-z0-9.-]*)\s*$/.exec(part);
156
+ if (next) citations.push({ id: next[1], parts: [] });
157
+ else if (part.trim()) citations[citations.length - 1].parts.push(part);
158
+ }
159
+ return citations.map(({ id, parts }) => ({
160
+ id,
161
+ locator: parts.join(';').replace(/\s+/g, ' ').trim(),
162
+ }));
163
+ }
164
+
165
+ /** Every source id a text cites, `[@a; @b]` included. */
166
+ export function citedIds(text) {
167
+ const ids = new Set();
168
+ for (const match of String(text).matchAll(CITATION_BRACKET)) {
169
+ for (const { id } of bracketCitations(match[1] ?? '', match[2])) ids.add(id);
170
+ }
171
+ return ids;
172
+ }
173
+
174
+ /**
175
+ * A Markdown link whose text holds a citation, `[as measured in [@id]](url)`:
176
+ * remark-citation leaves link text alone (a link cannot hold the marker's
177
+ * link), so the citation would print as typed. Group 1 is the link text.
178
+ */
179
+ const LINK_WITH_CITATION = /\[((?:[^[\]\n]|\[[^[\]\n]*\])*?\[@[^\]\n]*\](?:[^[\]\n]|\[[^[\]\n]*\])*)\]\(/g;
180
+
181
+ /**
182
+ * Each citation written inside a link's text, as `{ index, text }`: the
183
+ * offset of its `[@` in `text`, and the citation as written.
184
+ */
185
+ export function citationsInLinks(text) {
186
+ const found = [];
187
+ for (const link of String(text).matchAll(LINK_WITH_CITATION)) {
188
+ const start = (link.index ?? 0) + 1;
189
+ for (const citation of (link[1] ?? '').matchAll(/\[@[^\]\n]*\]/g)) {
190
+ found.push({ index: start + (citation.index ?? 0), text: citation[0] });
191
+ }
192
+ }
193
+ return found;
194
+ }
195
+
196
+ /**
197
+ * `text` with every citation inside a link's text blanked, offsets kept: what
198
+ * the page cites, for a scan that counts a lesson's sources.
199
+ */
200
+ export function withoutLinkCitations(text) {
201
+ let out = String(text);
202
+ for (const { index, text: citation } of citationsInLinks(out)) {
203
+ out = out.slice(0, index) + citation.replace(/[^\n]/g, ' ') + out.slice(index + citation.length);
204
+ }
205
+ return out;
206
+ }
207
+
@@ -1,10 +1,11 @@
1
- import { f as CourseConventions, g as CourseOperators, h as CourseRomanLabels, i as CourseAccents } from '../course-config-Dsnri9Tk.js';
2
- import { CurriculumCatalog } from '../curriculum/validation.js';
1
+ import { e as CourseOperators, f as CourseRomanLabels, g as CourseAccents } from '../course-config-rRtME5UV.js';
3
2
  import { NotationSpec } from '../compiler/collections.js';
4
3
  import { LintIgnore } from './lint-ignore.js';
5
4
  import { NotationRegistry, NotationRegistryInput } from './types.js';
6
5
  import '../rules/catalog.js';
7
6
  import '../rules/course-rules.js';
7
+ import '../curriculum/validation.js';
8
+ import './gate-retired-keys.js';
8
9
 
9
10
  /**
10
11
  * Corpus consistency checks (Step D6) — the Tier 1–2 checks from the cleanup
@@ -20,9 +21,7 @@ import '../rules/course-rules.js';
20
21
  * (a) glyph-unique-in-corpus one rendered glyph → one meaning corpus-wide
21
22
  * (b) notation-units / units-vocab every entry has units or `dimensionless`,
22
23
  * unit strings drawn from a controlled vocab
23
- * (c) convention-single-definition a sign / cash-flow-perspective convention a
24
- * lesson uses resolves to one declaration
25
- * (f) weak-local a `notation.local` symbol used only in its
24
+ * (f) weak-local a `notation.local` symbol used only in its
26
25
  * own defining equation and prose
27
26
  * (g) notation-source-locator every notation `sources` entry has a locator
28
27
  * (h) operator-explained a named operator a lesson uses is on the
@@ -45,7 +44,7 @@ import '../rules/course-rules.js';
45
44
  * `src/content/resolution-report.ts`.
46
45
  */
47
46
 
48
- type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'convention-single-definition' | 'weak-local' | 'notation-source-locator' | 'gloss-wants-promoting' | 'card-wants-demoting' | 'gloss-name-shape' | 'operator-explained' | 'roman-label-explained' | 'accent-explained' | 'operator-suspended' | 'operator-suspension-reason' | 'operator-shadowed';
47
+ type ConsistencyCode = 'glyph-unique-in-corpus' | 'notation-units' | 'units-vocab' | 'weak-local' | 'notation-source-locator' | 'gloss-wants-promoting' | 'card-wants-demoting' | 'gloss-name-shape' | 'operator-explained' | 'roman-label-explained' | 'accent-explained' | 'accent-swallowed' | 'operator-suspended' | 'operator-suspension-reason' | 'operator-shadowed';
49
48
  interface ConsistencyDiagnostic {
50
49
  readonly code: ConsistencyCode;
51
50
  /** Tier 3 — repairable violations are warnings; optional advice is info. */
@@ -62,10 +61,7 @@ interface ConsistencyInput {
62
61
  readonly registry: NotationRegistry;
63
62
  readonly notationInput: NotationRegistryInput;
64
63
  readonly specs: readonly NotationSpec[];
65
- readonly catalog: CurriculumCatalog;
66
64
  readonly lintIgnore: LintIgnore;
67
- /** Convention keys this course anchors on (from `content/course.config.ts`). */
68
- readonly conventions: CourseConventions;
69
65
  /** This course's edits to the named-operator baseline (rule N8). */
70
66
  readonly operators?: CourseOperators;
71
67
  /** This course's edits to the roman-label baseline (rule N10). */