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
@@ -1,4 +1,3 @@
1
- type EditorialStatus = 'draft' | 'in-review' | 'reviewed';
2
1
  interface SourceSpan {
3
2
  readonly file: string;
4
3
  readonly line?: number;
@@ -14,7 +13,13 @@ type NotationAlignment = {
14
13
  readonly kind: 'general';
15
14
  readonly rationale: string;
16
15
  };
17
- type NotationReferenceKind = 'prose' | 'math' | 'definition';
16
+ /**
17
+ * Where a reference was written: in prose, in math, in a definition's meaning
18
+ * or body, or in its `formula`. A formula's references bring their cards onto
19
+ * the page but are no dependency: two formulas may name each other
20
+ * (γ = 1 − α, α = 1 − γ).
21
+ */
22
+ type NotationReferenceKind = 'prose' | 'math' | 'definition' | 'formula';
18
23
  /**
19
24
  * A gloss resolved by the loader (D15). Declared on a card, keyed
20
25
  * `<card key>.<slug(name)>`, and deliberately absent from the registry: a
@@ -42,10 +47,17 @@ interface SharedNotationDefinitionInput {
42
47
  readonly domain: string;
43
48
  readonly formula?: string;
44
49
  readonly units?: string;
50
+ /** Rule N23: an index entry's title for what it indexes, `{base} …`. */
51
+ readonly reads?: string;
52
+ /** Rule N23: the set a running index letter runs over. */
53
+ readonly range?: string;
54
+ /** Rule N23: as an index, a fixed label rather than a running position. */
55
+ readonly fixedIndex?: true;
56
+ /** Rule N23: an index entry's `formula` as written, `{base}` unbound. */
57
+ readonly formulaTemplate?: string;
45
58
  readonly sources: readonly string[];
46
59
  readonly seeAlso: readonly string[];
47
60
  readonly alignment: NotationAlignment;
48
- readonly status: EditorialStatus;
49
61
  readonly aiAssisted: boolean;
50
62
  readonly body: string;
51
63
  /** References harvested from the Markdown body. */
@@ -61,6 +73,14 @@ interface LocalNotationDefinitionInput {
61
73
  readonly summary: string;
62
74
  readonly formula?: string;
63
75
  readonly units?: string;
76
+ /** Rule N23: an index entry's title for what it indexes, `{base} …`. */
77
+ readonly reads?: string;
78
+ /** Rule N23: the set a running index letter runs over. */
79
+ readonly range?: string;
80
+ /** Rule N23: as an index, a fixed label rather than a running position. */
81
+ readonly fixedIndex?: true;
82
+ /** Rule N23: an index entry's `formula` as written, `{base}` unbound. */
83
+ readonly formulaTemplate?: string;
64
84
  readonly sources: readonly string[];
65
85
  readonly seeAlso: readonly string[];
66
86
  readonly alignment: NotationAlignment;
@@ -83,7 +103,6 @@ interface NotationSuspension {
83
103
  }
84
104
  interface NotationLessonInput {
85
105
  readonly lessonId: string;
86
- readonly status: EditorialStatus;
87
106
  readonly localDefinitions: readonly LocalNotationDefinitionInput[];
88
107
  /**
89
108
  * Decorations this page suspends (`notation.suspend`, rule N22). Optional
@@ -122,7 +141,7 @@ interface AbbreviationDefinitionInput {
122
141
  readonly domain?: string;
123
142
  readonly source: SourceSpan;
124
143
  }
125
- type NotationDiagnosticCode = 'invalid-key' | 'duplicate-page' | 'duplicate-definition' | 'conflicting-definition' | 'undefined-reference' | 'reference-cycle' | 'unused-definition' | 'alignment-unknown-competency' | 'alignment-unknown-lesson' | 'alignment-introduction' | 'alignment-unavailable' | 'alignment-review-state' | 'gloss-collides-with-card';
144
+ type NotationDiagnosticCode = 'invalid-key' | 'duplicate-page' | 'duplicate-definition' | 'conflicting-definition' | 'undefined-reference' | 'reference-cycle' | 'unused-definition' | 'local-shadows-shared' | 'malformed-reference' | 'alignment-unknown-competency' | 'alignment-unknown-lesson' | 'alignment-introduction' | 'alignment-unavailable' | 'gloss-collides-with-card';
126
145
  type DiagnosticSeverity = 'error' | 'warning' | 'info';
127
146
  interface NotationDiagnostic {
128
147
  readonly code: NotationDiagnosticCode;
@@ -159,9 +178,16 @@ interface NotationDefinitionRecordBase {
159
178
  readonly scope: NotationDefinitionScope;
160
179
  readonly notation: string;
161
180
  readonly label: string;
181
+ /** Rule N23: an index entry's title for what it indexes, `{base} …`. */
182
+ readonly reads?: string;
183
+ /** Rule N23: the set a running index letter runs over. */
184
+ readonly range?: string;
185
+ /** Rule N23: as an index, a fixed label rather than a running position. */
186
+ readonly fixedIndex?: true;
187
+ /** Rule N23: an index entry's `formula` as written, `{base}` unbound. */
188
+ readonly formulaTemplate?: string;
162
189
  readonly seeAlso: readonly string[];
163
190
  readonly alignment: NotationAlignment;
164
- readonly status: EditorialStatus;
165
191
  readonly source: SourceSpan;
166
192
  readonly resolvedReferences: readonly ResolvedNotationReference[];
167
193
  }
@@ -191,7 +217,6 @@ interface NotationBinding {
191
217
  }
192
218
  interface NotationPageBundle {
193
219
  readonly lessonId: string;
194
- readonly status: EditorialStatus;
195
220
  readonly source: SourceSpan;
196
221
  readonly bindings: readonly NotationBinding[];
197
222
  readonly definitionIds: readonly string[];
@@ -208,5 +233,36 @@ interface NotationRegistry {
208
233
  readonly backlinks: readonly NotationBacklink[];
209
234
  readonly diagnostics: readonly NotationDiagnostic[];
210
235
  }
236
+ /** A derived card a math source uses (rule N23): `key` is `<base>.sub.<index>`. */
237
+ interface DerivedUse {
238
+ readonly key: string;
239
+ readonly base: string;
240
+ readonly index: string;
241
+ }
242
+ /**
243
+ * How one symbol with a subscript is explained (rule N23), as
244
+ * `resolveMathGlyphs` reports it in `indices`.
245
+ */
246
+ interface IndexedSymbol {
247
+ readonly token: string;
248
+ readonly start: number;
249
+ readonly end: number;
250
+ readonly base: string;
251
+ readonly index: string;
252
+ readonly how: 'whole-form' | 'family' | 'derived' | 'inside-form' | 'explicit' | 'swallowed' | 'inside-wrap' | 'composed' | 'unresolved';
253
+ readonly key: string | null;
254
+ readonly explained: boolean;
255
+ readonly baseExplained: boolean;
256
+ /** The parts no card explains: the base first, when it has none, then each slot. */
257
+ readonly unexplained: readonly string[];
258
+ /** Running index letters (`i`, `k`, `t`) in its slots, for the index-set check. */
259
+ readonly running: readonly {
260
+ readonly letter: string;
261
+ readonly start: number;
262
+ readonly end: number;
263
+ readonly key: string | null;
264
+ readonly cover: string | null;
265
+ }[];
266
+ }
211
267
 
212
- export type { AbbreviationDefinitionInput, DiagnosticSeverity, EditorialStatus, LocalNotationDefinitionInput, LocalNotationDefinitionRecord, NotationAlignment, NotationBacklink, NotationBinding, NotationDefinitionRecord, NotationDefinitionScope, NotationDiagnostic, NotationDiagnosticCode, NotationGlossEntry, NotationLessonInput, NotationPageBundle, NotationReferenceInput, NotationReferenceKind, NotationRegistry, NotationRegistryInput, NotationSuspension, ResolvedNotationReference, SharedNotationDefinitionInput, SharedNotationDefinitionRecord, SourceSpan };
268
+ export type { AbbreviationDefinitionInput, DerivedUse, DiagnosticSeverity, IndexedSymbol, LocalNotationDefinitionInput, LocalNotationDefinitionRecord, NotationAlignment, NotationBacklink, NotationBinding, NotationDefinitionRecord, NotationDefinitionScope, NotationDiagnostic, NotationDiagnosticCode, NotationGlossEntry, NotationLessonInput, NotationPageBundle, NotationReferenceInput, NotationReferenceKind, NotationRegistry, NotationRegistryInput, NotationSuspension, ResolvedNotationReference, SharedNotationDefinitionInput, SharedNotationDefinitionRecord, SourceSpan };
@@ -20,6 +20,7 @@ const GATE_MATH = { module: "src/reference/gate-math.ts", fn: "resolveInto" };
20
20
  const GLYPHS = { module: "src/reference/math-glyphs.mjs", fn: "resolveMathGlyphs" };
21
21
  const REGISTRY = { module: "src/reference/registry.ts", fn: "buildNotationRegistry" };
22
22
  const KEYED_FLOATS = { module: "src/reference/keyed-float-validate.ts", fn: "validateKeyedFloats" };
23
+ const INDICES = { module: "src/reference/gate-indices.ts", fn: "gateIndices" };
23
24
  function consistency(fn) {
24
25
  return { module: "src/reference/consistency.ts", fn };
25
26
  }
@@ -35,10 +36,28 @@ function keyedFloatChecks() {
35
36
  check(`${prefix}-duplicate-key`, "error", false, emitter),
36
37
  check(`${prefix}-ref-resolves`, "error", false, emitter)
37
38
  );
39
+ if (prefix === "eq") {
40
+ out.push(
41
+ check("eq-label-repeated", "error", false, emitter),
42
+ // LaTeX's `\eqref{eq:key}` in prose, which MDX cannot parse.
43
+ check("eq-latex-ref", "error", false, emitter)
44
+ );
45
+ }
38
46
  if (prefix === "tab" || prefix === "fig" || prefix === "dia" || prefix === "ex") {
39
47
  out.push(check(`${prefix}-key-unused`, "info", false, emitter));
40
48
  out.push(check(`${prefix}-removed-prop`, "error", false, emitter));
41
49
  }
50
+ if (prefix !== "eq" && prefix !== "check") {
51
+ out.push(check(`${prefix}-unread-prop`, "warning", false, emitter));
52
+ }
53
+ if (prefix === "tab") {
54
+ out.push(
55
+ check("tab-several-tables", "error", false, {
56
+ module: "src/reference/tables-validate.ts",
57
+ fn: "validateTables"
58
+ })
59
+ );
60
+ }
42
61
  }
43
62
  out.push(
44
63
  check("ex-missing-prop", "error", false, {
@@ -50,6 +69,10 @@ function keyedFloatChecks() {
50
69
  check("sec-ref-resolves", "error", true, {
51
70
  module: "src/reference/sections-validate.ts",
52
71
  fn: "validateSectionRefs"
72
+ }),
73
+ check("heading-anchor-collision", "error", true, {
74
+ module: "src/reference/sections-validate.ts",
75
+ fn: "validateHeadingAnchors"
53
76
  })
54
77
  );
55
78
  out.push(
@@ -161,7 +184,7 @@ const RULES = [
161
184
  title: "A physical quantity, unit, or math operator is typeset in `$\u2026$`, never as plain text \u2014 in prose, a table cell, an assessment `prompt` / `explanation`, or a caption that itself lives outside math.",
162
185
  elements: ["prose", "assessment", "table"],
163
186
  doc: "notation.md#n9",
164
- summary: 'Never write `45 m/s`, `t (s)`, `vx` or `KE/m` as plain text, and never spell a stated quantity\'s unit out: "4 seconds", "2 meters per second squared", "30 degrees", "$x = 3$ meters" are `$4\\ \\text{s}$`, `$2\\ \\text{m/s}^2$`, `$30^{\\circ}$`, `$x = 3\\ \\text{m}$`. A unit inside math is `\\text{}`-wrapped, short form, in the same span as its number or symbol. A unit named on its own ("measured in meters") stays a word; a sentence that defines a unit is waived. JSX prop strings (`caption="\u2026"`) and assessment choice labels render no math, so N9 does not apply there.',
187
+ summary: 'Never write `45 m/s`, `t (s)`, `vx` or `KE/m` as plain text, and never spell a stated quantity\'s unit out: "4 seconds", "2 meters per second squared", "30 degrees", "$x = 3$ meters" are `$4\\ \\text{s}$`, `$2\\ \\text{m/s}^2$`, `$30^{\\circ}$`, `$x = 3\\ \\text{m}$`. A unit inside math is `\\text{}`-wrapped, short form, in the same span as its number or symbol. A unit named on its own ("measured in meters") stays a word; a sentence that defines a unit is waived. JSX prop strings (`caption="\u2026"`) render no math, so N9 does not apply there; nor does it apply to an assessment choice label, which may still hold math (`$30^{\\circ}$`).',
165
188
  checks: [
166
189
  check(
167
190
  "prose-notation",
@@ -314,7 +337,7 @@ const RULES = [
314
337
  title: "A notation entry states its `units`, or declares itself `dimensionless`, using the units vocabulary.",
315
338
  elements: ["notation"],
316
339
  doc: "notation.md#n19",
317
- summary: "Every notation entry has `units:` from `units-vocab.ts` (`m/s`, `s`, `kg`, \u2026) or `dimensionless: true`. A token outside the vocabulary is a warning.",
340
+ summary: 'Every notation entry has `units:` from `units-vocab.ts` (`m/s`, `s`, `kg`, \u2026) or `dimensionless: true`, except an index entry (`latex: "{}_0"`, N23), which takes neither. A token outside the vocabulary is a warning.',
318
341
  checks: [
319
342
  check("notation-units", "warning", false, consistency("checkNotationUnits")),
320
343
  check("units-vocab", "warning", false, consistency("checkNotationUnits"))
@@ -339,7 +362,7 @@ const RULES = [
339
362
  title: "A gloss is named by a short noun phrase, not described by a sentence.",
340
363
  elements: ["notation"],
341
364
  doc: "notation.md#n21",
342
- summary: "A gloss `label` names the symbol in a few words (\u201Claunch angle\u201D); a clause, sentence punctuation, or a definition belongs in a full notation entry instead.",
365
+ summary: "A gloss `name` names the symbol in a few words (\u201Claunch angle\u201D); a clause, sentence punctuation, or a definition belongs in a full notation entry instead.",
343
366
  checks: [check("gloss-name-shape", "warning", false, consistency("checkGlossNameShape"))]
344
367
  },
345
368
  {
@@ -352,6 +375,7 @@ const RULES = [
352
375
  summary: "A decoration (`\\hat`, `\\bar`, `\\overline`, `'`, `^*`, `^\\dagger`, \u2026) is on the course\u2019s `allow` list (shipped empty), explained by an operator entry (`latex: \\hat{}`), or the decorated form has its own entry (`latex: \\hat{\\beta}`), which wins for that form. A page may suspend a decoration in `notation.suspend`, with a `reason`; every decorated form of it there then needs its own entry.",
353
376
  checks: [
354
377
  check("accent-explained", "warning", false, consistency("checkAccentExplained")),
378
+ check("accent-swallowed", "warning", false, consistency("checkAccentExplained")),
355
379
  check("suspended-operator-unresolved", "error", true, GATE_MATH),
356
380
  check("operator-suspension-reason", "warning", false, consistency("checkOperatorSuspension")),
357
381
  check("operator-suspended", "info", false, consistency("checkOperatorSuspension")),
@@ -359,6 +383,22 @@ const RULES = [
359
383
  ],
360
384
  settings: baselineEdits("Decorations")
361
385
  },
386
+ {
387
+ kind: "rule",
388
+ id: "N23",
389
+ short: "An index is part of the symbol: whole form, family or index entry",
390
+ title: "An index is part of the symbol: a subscript is explained by the indexed symbol\u2019s entry, a family entry, or an index entry.",
391
+ elements: ["math", "notation"],
392
+ doc: "notation.md#n23",
393
+ summary: 'A subscript on a symbol is explained by the indexed symbol\u2019s own entry (`latex: v_0`), a family entry whose index runs (`latex: x_k` explains `x_1`), or an index entry for the subscript (`latex: "{}_0"`, which composes a derived card); a card on the base alone does not explain it, and arithmetic on an index letter with its own card does (`X_{t-1}`). A running index \u2014 a lowercase letter not marked `fixedIndex` \u2014 states its set, as `range` on its card or after a `\\quad` on the first display line that uses it. Never wrap an indexed symbol in `\\explain` with a key whose `latex` has no index.',
394
+ checks: [
395
+ check("index-explained", "error", true, INDICES),
396
+ check("index-swallowed", "error", true, INDICES),
397
+ check("index-range", "error", true, INDICES),
398
+ check("derived-key-taken", "error", true, INDICES)
399
+ ],
400
+ unchecked: "whether a `\\quad` clause states the set correctly; whether a lowercase index is really running; lab math whose glyph table is a copy of the one `labMathScope` returned."
401
+ },
362
402
  // ---- V — visualizations -----------------------------------------------------
363
403
  {
364
404
  kind: "rule",
@@ -387,8 +427,18 @@ const RULES = [
387
427
  title: "Interactive figures: a hydrated island and the `renderLabMath` seam for equations.",
388
428
  elements: ["plot"],
389
429
  doc: "visualizations.md#v3",
390
- summary: "An interactive figure is an island under `content/labs/` inside a `<Figure>`, renders its equations through `renderLabMath`, and labels its controls; the figure\u2019s caption is its name, so the lab writes no accessible name or text alternative of its own.",
391
- checks: []
430
+ summary: "An interactive figure is an island under `content/labs/` inside a `<Figure>`, renders its equations through `renderLabMath` (`{ display: false }` for a legend, an axis title or a label; a `{ math }` run for the caption clause) and never KaTeX directly, and labels its controls; the figure\u2019s caption is its name, so the lab writes no accessible name or text alternative of its own. Its default picture is drawn at build time too and handed to the island as children, so the built page has it.",
431
+ checks: [
432
+ check("lab-katex-direct", "error", false, {
433
+ module: "src/reference/lab-katex-validate.ts",
434
+ fn: "validateLabKatexImports"
435
+ }),
436
+ check("lab-picture-undrawn", "warning", false, {
437
+ module: "src/reference/figure-pictures.ts",
438
+ fn: "undrawnFigurePictures"
439
+ })
440
+ ],
441
+ unchecked: "that a lab labels its controls and writes no name of its own, and math a lab draws without importing `katex` \u2014 the build\u2019s rendered check (`figure-symbols.ts`) covers the page it renders."
392
442
  },
393
443
  {
394
444
  kind: "rule",
@@ -436,6 +486,53 @@ const RULES = [
436
486
  }
437
487
  ]
438
488
  },
489
+ {
490
+ kind: "rule",
491
+ id: "V7",
492
+ short: "A figure\u2019s picture fits on one screen",
493
+ title: "A figure\u2019s picture takes at most `maxPercent` of the screen height.",
494
+ elements: ["plot"],
495
+ doc: "visualizations.md#v7",
496
+ summary: "The default slot of a `<Figure>` \u2014 plot, key and tables \u2014 stays within `maxPercent` of the viewport height, so the reader sees the picture and its caption line together. A square or tall plot gets a fixed width instead of the full column.",
497
+ checks: [
498
+ check("figure-too-tall", "warning", false, {
499
+ module: "src/reference/figure-fit.ts",
500
+ fn: "checkFigureFit"
501
+ })
502
+ ],
503
+ unchecked: "any page or viewport the course\u2019s Playwright suite does not open \u2014 the check runs in the browser, not in the build.",
504
+ settings: [
505
+ {
506
+ key: "maxPercent",
507
+ title: "Largest picture height (% of screen)",
508
+ description: "How much of the viewport height a figure\u2019s picture may take.",
509
+ schema: { type: "integer", minimum: 10, default: 80 }
510
+ }
511
+ ]
512
+ },
513
+ {
514
+ kind: "rule",
515
+ id: "V8",
516
+ short: "A diagram fits the lesson column",
517
+ title: "A diagram is no wider than the lesson column.",
518
+ elements: ["diagram"],
519
+ doc: "visualizations.md#v8",
520
+ summary: 'A `<Diagram>` lays out no wider than `columnWidth`, the lesson column at a 1366px-wide window, so the reader sees the whole graph without scrolling it. Narrow it with `direction="TB"`, a shorter label or a node `width` (its label wraps inside it); `warnAboveWidth={false}` marks a diagram meant to scroll.',
521
+ checks: [
522
+ check("dia-too-wide", "warning", false, {
523
+ module: "src/reference/diagram-fit.ts",
524
+ fn: "oversizedDiagrams"
525
+ })
526
+ ],
527
+ settings: [
528
+ {
529
+ key: "columnWidth",
530
+ title: "Column width (px)",
531
+ description: "The widest a diagram may lay out: the lesson column at the window width the course is read at most (707px at 1366px).",
532
+ schema: { type: "integer", minimum: 200, default: 707 }
533
+ }
534
+ ]
535
+ },
439
536
  // ---- C — versioning ---------------------------------------------------------
440
537
  {
441
538
  kind: "rule",
@@ -555,7 +652,7 @@ const RULES = [
555
652
  title: 'A table is a `<Table id="\u2026">`, never a raw Markdown or HTML table.',
556
653
  elements: ["table"],
557
654
  doc: "structure-rules.md#s3",
558
- summary: 'Wrap every pipe table or `<table>` in `<Table id="\u2026" caption="\u2026">` so it gets a number and `[[tab-\u2026]]` can reach it.',
655
+ summary: 'Wrap every pipe table or `<table>` in `<Table id="\u2026" caption="\u2026">` so it gets a number and `[[tab-\u2026]]` can reach it; a table in a worked example stays plain.',
559
656
  checks: [
560
657
  check("raw-table-markdown", "warning", false, {
561
658
  module: "src/reference/raw-floats-validate.ts",
@@ -586,23 +683,6 @@ const RULES = [
586
683
  })
587
684
  ]
588
685
  },
589
- {
590
- kind: "rule",
591
- id: "S5",
592
- short: "A sign / perspective convention is declared once and cited elsewhere",
593
- title: "A sign or perspective convention is declared once and cited everywhere else.",
594
- elements: ["prose"],
595
- doc: "structure-rules.md#s5",
596
- summary: "Prose that states a sign / cash-flow-perspective convention resolves it to the single declaration \u2014 the `conventions.signConventionKey` notation entry (`[[key]]`) or the `conventions.cashFlowPerspectiveCompetency` competency \u2014 rather than restating it.",
597
- checks: [
598
- check(
599
- "convention-single-definition",
600
- "warning",
601
- false,
602
- consistency("checkConventionSingleDefinition")
603
- )
604
- ]
605
- },
606
686
  {
607
687
  kind: "rule",
608
688
  id: "S6",
@@ -615,6 +695,12 @@ const RULES = [
615
695
  check("heading-skip", "error", true, {
616
696
  module: "src/reference/gate-heading-structure.ts",
617
697
  fn: "gateHeadingStructure"
698
+ }),
699
+ // A knowledge check or a worked-example set, which draws an `<h2>`,
700
+ // inside a memo or a short note.
701
+ check("heading-nested", "error", true, {
702
+ module: "src/reference/gate-heading-structure.ts",
703
+ fn: "gateHeadingStructure"
618
704
  })
619
705
  ]
620
706
  },
@@ -653,6 +739,30 @@ const RULES = [
653
739
  summary: 'Give each figure, table and diagram a `short` caption that names it: it opens the caption line in bold, and names the float in "Hidden here", a copied link and the "Figures and tables" page. The long `caption` goes on from that name to say what the float shows \u2014 its subject, its marks and symbols, the case shown \u2014 and at most the one thing to notice. The argument drawn from a figure is lesson text after it, and a lab\u2019s instructions are the first line of its `tools`.',
654
740
  checks: []
655
741
  },
742
+ {
743
+ kind: "rule",
744
+ id: "S10",
745
+ short: "A page that is not a chapter explains its symbols but numbers nothing: it links a chapter\u2019s float instead",
746
+ title: "A page that is not a chapter \u2014 the landing page, a part\u2019s overview page \u2014 explains its symbols like a chapter, but carries no numbered float: it links the chapter\u2019s float instead.",
747
+ elements: ["part-page", "plot", "table", "diagram", "equation", "notation"],
748
+ doc: "structure-rules.md#s10",
749
+ summary: "On `content/docs/index.mdx`, a `content/docs/<part>/index.mdx`, or any page outside a part: `[[key]]`, `\\explain`, math, citations and `[[chapter#tab-key]]` links work and open cards, and the math and prose gates apply. No `<Table>`, `<Figure>`, `<Diagram>`, `<CompactExamples>`, `<AssessmentSet>`, lab, `\\label{eq:\u2026}` or `notation.local`/`notation.suspend`; no `$\u2026$` or `[[\u2026]]` in `hero.title`, `hero.tagline` or a `<PartOverview chain>` step.",
750
+ checks: [
751
+ check("non-chapter-float", "error", true, {
752
+ module: "src/reference/gate-non-chapter-pages.ts",
753
+ fn: "gateNonChapterPages"
754
+ }),
755
+ check("non-chapter-local-notation", "error", false, {
756
+ module: "src/reference/gate-non-chapter-pages.ts",
757
+ fn: "gateNonChapterPages"
758
+ }),
759
+ check("non-chapter-plain-text", "error", false, {
760
+ module: "src/reference/gate-non-chapter-pages.ts",
761
+ fn: "gateNonChapterPages"
762
+ })
763
+ ],
764
+ locked: "a float needs a chapter number, and such a page has none \u2014 turning the rule off would not render one."
765
+ },
656
766
  // ---- P — prose --------------------------------------------------------------
657
767
  {
658
768
  kind: "rule",
@@ -787,6 +897,13 @@ const RETIRED = [
787
897
  date: "2026-09-26",
788
898
  into: "C3",
789
899
  why: "The release procedure and the invariant it keeps are one rule, and `knowledge-changelog-missing` (owned by C3) was already the check that fired on a half-finished release."
900
+ },
901
+ {
902
+ id: "S5",
903
+ title: "A sign or perspective convention is declared once and cited everywhere else.",
904
+ date: "2026-10-07",
905
+ into: "N4",
906
+ why: "Its check knew one finance course\u2019s phrasing and one key per course, and never fired in any course; the `conventions` setting it read is gone. A convention is a definition, so it is now N4\u2019s: defined once on its notation entry, whose `meaning` names the other choice and whose body says how to convert."
790
907
  }
791
908
  ];
792
909
  const INVARIANTS = [
@@ -796,7 +913,7 @@ const INVARIANTS = [
796
913
  title: "Notation keys are well-formed, unique, defined, and acyclic.",
797
914
  elements: ["notation", "math"],
798
915
  doc: "components.md#reference-systems",
799
- summary: "A `[[key]]` or `\\explain{key}{\u2026}` names an existing entry; a key is kebab-case; a shared entry is defined once; formula references never form a cycle.",
916
+ summary: "A `[[key]]` or `\\explain{key}{\u2026}` names an existing entry; a key is kebab-case; a shared entry is defined once; references in meanings and bodies never form a cycle (formulas may name each other).",
800
917
  checks: [
801
918
  check("undefined-reference", "error", false, REGISTRY),
802
919
  check("invalid-key", "error", false, REGISTRY, GLYPHS),
@@ -811,7 +928,11 @@ const INVARIANTS = [
811
928
  fn: "reportUnknownScopeKeys"
812
929
  }),
813
930
  check("malformed-explicit-binding", "error", true, GLYPHS),
814
- check("unused-definition", "info", false, REGISTRY)
931
+ check("unused-definition", "info", false, REGISTRY),
932
+ // A page-local entry under a shared key: two meanings for one key.
933
+ check("local-shadows-shared", "warning", false, REGISTRY),
934
+ // A `[[` that never closes, before the render fails on it.
935
+ check("malformed-reference", "error", false, REGISTRY)
815
936
  ]
816
937
  },
817
938
  {
@@ -830,7 +951,12 @@ const INVARIANTS = [
830
951
  check("ambiguous-span", "error", true, GLYPHS),
831
952
  check("ambiguous-base-fallback", "error", true, GLYPHS),
832
953
  check("ambiguous-canonical-notation", "error", false, GLYPHS),
833
- check("math-compile-error", "error", true, GATE_MATH)
954
+ check("math-compile-error", "error", true, GATE_MATH),
955
+ // `$5 to $6`: money remark-math reads as one math span.
956
+ check("currency-dollar", "error", true, {
957
+ module: "src/reference/gate-math.ts",
958
+ fn: "gateContentMath"
959
+ })
834
960
  ]
835
961
  },
836
962
  {
@@ -848,7 +974,7 @@ const INVARIANTS = [
848
974
  title: "The course settings name real rules and current keys.",
849
975
  elements: ["changelog"],
850
976
  doc: "README.md#amending",
851
- summary: "A `rules.<ID>` block names a live rule; a deprecated top-level key (`operators`, `romanLabels`, `abbreviations`) still works but warns until it is moved under `rules`.",
977
+ summary: "A `rules.<ID>` block names a live rule; a deprecated top-level key (`operators`, `romanLabels`, `abbreviations`) still works but warns until it is moved under `rules`; `conventions` is ignored and warns until it is deleted.",
852
978
  checks: [
853
979
  check("config-deprecated-key", "warning", false, {
854
980
  module: "src/rules/settings.ts",
@@ -892,8 +1018,10 @@ const INVARIANTS = [
892
1018
  "track-order",
893
1019
  "assessment-coverage",
894
1020
  "invalid-assessment",
895
- "review-state",
896
- "teaches-support"
1021
+ "missing-assumptions",
1022
+ "teaches-support",
1023
+ // `[text [@id]](url)`: a citation the page prints as typed.
1024
+ "citation-in-link"
897
1025
  ].map(
898
1026
  (code) => check(code, "error", false, {
899
1027
  module: "src/curriculum/validation.ts",
@@ -904,14 +1032,77 @@ const INVARIANTS = [
904
1032
  "alignment-unknown-competency",
905
1033
  "alignment-unknown-lesson",
906
1034
  "alignment-introduction",
907
- "alignment-unavailable",
908
- "alignment-review-state"
1035
+ "alignment-unavailable"
909
1036
  ].map(
910
1037
  (code) => check(code, "error", false, {
911
1038
  module: "src/reference/curriculum-alignment.ts",
912
1039
  fn: "validateNotationAlignment"
913
1040
  })
914
- )
1041
+ ),
1042
+ // A course's parts and the lessons in them (`compileManifest`): a
1043
+ // lesson in a directory no part lists, and a part with nothing in it yet.
1044
+ check("lesson-outside-parts", "error", false, {
1045
+ module: "src/compiler/manifest.ts",
1046
+ fn: "compileManifest"
1047
+ }),
1048
+ check("part-empty", "warning", false, {
1049
+ module: "src/compiler/manifest.ts",
1050
+ fn: "compileManifest"
1051
+ })
1052
+ ]
1053
+ },
1054
+ {
1055
+ kind: "invariant",
1056
+ id: "content-schema",
1057
+ title: "Every content file satisfies its collection\u2019s schema.",
1058
+ elements: ["notation", "assessment", "source", "changelog"],
1059
+ doc: "structure.md#content-schemas",
1060
+ summary: "A page\u2019s explico fields, a notation entry, an abbreviation, a competency, an assessment, a source and a track match the schema `astro build` loads them with (`compiler/content-schemas.ts`); `validate:content` reports the field and its line.",
1061
+ checks: [
1062
+ check("content-schema", "error", false, {
1063
+ module: "src/compiler/content-schema.ts",
1064
+ fn: "validateContentSchemas"
1065
+ })
1066
+ ]
1067
+ },
1068
+ {
1069
+ kind: "invariant",
1070
+ id: "retired-keys",
1071
+ title: "No content file sets a key the engine has retired or cannot honour.",
1072
+ elements: ["changelog"],
1073
+ doc: "structure.md#retired-keys",
1074
+ summary: "A frontmatter or JSON key explico no longer reads (`editorialStatus`) fails the build at every file that still sets it; delete the line. A lesson\u2019s `draft: true` fails it too (the sidebar would name a page the build drops), and its `sidebar.order` warns (chapters follow the tracks).",
1075
+ checks: [
1076
+ check("retired-key", "error", false, {
1077
+ module: "src/reference/gate-retired-keys.ts",
1078
+ fn: "findRetiredKeys"
1079
+ }),
1080
+ check("unsupported-key", "error", false, {
1081
+ module: "src/reference/gate-retired-keys.ts",
1082
+ fn: "findUnsupportedKeys"
1083
+ }),
1084
+ check("ignored-key", "warning", false, {
1085
+ module: "src/reference/gate-retired-keys.ts",
1086
+ fn: "findUnsupportedKeys"
1087
+ })
1088
+ ]
1089
+ },
1090
+ {
1091
+ kind: "invariant",
1092
+ id: "prop-text",
1093
+ title: "A component\u2019s quoted prop holds plain text, and prose holds no `{\u2026}` expression.",
1094
+ elements: ["math", "plot", "diagram", "table"],
1095
+ doc: "components.md#props-are-plain-text",
1096
+ summary: 'A quoted prop (`caption="\u2026"`, `short="\u2026"`, `title="\u2026"`, `label="\u2026"`) reaches the page as plain text: no KaTeX, reference or citation runs in it. `$\u2026$`, `[[\u2026]]` or `[@\u2026]` in one fails the build, since it would print as typed; write the symbol in Unicode or words, and reference or cite from the prose next to the component. MDX reads `{\u2026}` in prose as JavaScript (`{a}` fails the build, "a is not defined"): write `\\{a\\}`, or `$\\{a\\}$` for a set.',
1097
+ checks: [
1098
+ check("markup-in-prop", "error", false, {
1099
+ module: "src/reference/gate-prop-text.ts",
1100
+ fn: "findMarkupInProps"
1101
+ }),
1102
+ check("mdx-expression", "error", false, {
1103
+ module: "src/reference/gate-mdx-expression.ts",
1104
+ fn: "gateMdxExpressions"
1105
+ })
915
1106
  ]
916
1107
  }
917
1108
  ];
@@ -1,5 +1,5 @@
1
1
  import { Element, Enforcement } from './catalog.js';
2
- import { M as ManifestRules } from '../course-config-Dsnri9Tk.js';
2
+ import { M as ManifestRules } from '../course-config-rRtME5UV.js';
3
3
  import './course-rules.js';
4
4
 
5
5
  /** The slice of a manifest the page needs. */