chemicalml 0.2.0 → 0.3.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 (450) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +7 -1
  3. data/.rubocop.yml +23 -0
  4. data/CLAUDE.md +95 -40
  5. data/README.adoc +154 -26
  6. data/TODO.align/36-molecular-convention-coverage.md +46 -0
  7. data/TODO.align/37-compchem-convention-coverage.md +55 -0
  8. data/TODO.align/38-dictionary-convention-coverage.md +34 -0
  9. data/TODO.align/39-unit-dictionary-convention-coverage.md +33 -0
  10. data/TODO.align/40-unit-type-dictionary-convention-coverage.md +28 -0
  11. data/TODO.align/41-expand-canonical-model-layer.md +43 -0
  12. data/TODO.align/42-translator-wire-new-models.md +31 -0
  13. data/TODO.align/43-dictionary-model-no-to-h.md +39 -0
  14. data/TODO.align/44-schema3-only-and-schema24-module.md +44 -0
  15. data/TODO.align/45-documentation-and-final-verification.md +23 -0
  16. data/TODO.align/46-lattice-vector-content.md +24 -0
  17. data/TODO.align/47-molecule-crystal-spectra-property-lists.md +23 -0
  18. data/TODO.align/48-model-list-and-module-lists.md +21 -0
  19. data/TODO.align/49-more-chemistry-models.md +34 -0
  20. data/TODO.align/50-validation-report.md +29 -0
  21. data/TODO.align/51-convention-auto-detection.md +30 -0
  22. data/TODO.align/52-schema24-legacy-elements.md +28 -0
  23. data/TODO.align/53-round-trip-integration-specs.md +30 -0
  24. data/TODO.align/54-document-versioned-parser-shim.md +38 -0
  25. data/TODO.align/55-molecule-missing-attributes.md +20 -0
  26. data/TODO.align/56-geometry-models.md +24 -0
  27. data/TODO.align/57-compchem-models.md +26 -0
  28. data/TODO.align/58-remaining-models.md +24 -0
  29. data/TODO.align/59-wire-new-models-translator.md +18 -0
  30. data/TODO.align/60-docs-and-final-verification.md +19 -0
  31. data/TODO.align/61-parallel-array-attrs.md +54 -0
  32. data/TODO.align/62-per-element-attribute-gaps.md +49 -0
  33. data/TODO.align/63-universal-children-mixin.md +42 -0
  34. data/TODO.align/64-per-element-child-gaps.md +81 -0
  35. data/TODO.align/65-simple-unit-convention.md +40 -0
  36. data/TODO.align/66-schema24-legacy-children.md +32 -0
  37. data/TODO.align/67-final-verification.md +32 -0
  38. data/TODO.align/68-detection-coverage.md +33 -0
  39. data/TODO.align/69-json-yaml-roundtrip.md +27 -0
  40. data/TODO.align/70-iterative-walker.md +40 -0
  41. data/TODO.align/71-violation-value.md +25 -0
  42. data/TODO.align/72-close-child-gaps.md +66 -0
  43. data/TODO.align/73-schema24-parse-limitation.md +64 -0
  44. data/TODO.align/74-final-verification-round18.md +15 -0
  45. data/TODO.align/75-key-value-mappings.md +43 -0
  46. data/TODO.align/76-schema24-workaround.md +57 -0
  47. data/TODO.align/77-convention-coverage-matrix.md +31 -0
  48. data/TODO.align/78-fixture-audit.md +23 -0
  49. data/TODO.align/79-final-verification-round19.md +15 -0
  50. data/TODO.align/80-xsd-enum-constants.md +35 -0
  51. data/TODO.align/81-enum-validation-constraints.md +29 -0
  52. data/TODO.align/82-reference-resolver.md +33 -0
  53. data/TODO.align/83-toml-roundtrip.md +20 -0
  54. data/TODO.align/84-final-verification-round21.md +16 -0
  55. data/TODO.align/85-bond-distinct-atoms.md +25 -0
  56. data/TODO.align/86-references-should-resolve.md +20 -0
  57. data/TODO.align/87-cli-utility.md +26 -0
  58. data/TODO.align/88-diverse-fixtures.md +26 -0
  59. data/TODO.align/89-final-verification-round22.md +15 -0
  60. data/TODO.align/90-bond-stereo-completeness.md +31 -0
  61. data/TODO.align/91-atomparity-distinct.md +21 -0
  62. data/TODO.align/92-unit-dictionary-completeness.md +28 -0
  63. data/TODO.align/93-yard-docs.md +23 -0
  64. data/TODO.align/94-final-verification-round23.md +15 -0
  65. data/TODO.align/README.round17.md +28 -0
  66. data/TODO.align/README.round18.md +37 -0
  67. data/TODO.align/README.round19.md +40 -0
  68. data/TODO.align/README.round20.md +49 -0
  69. data/TODO.align/README.round21.md +35 -0
  70. data/TODO.align/README.round22.md +48 -0
  71. data/TODO.align/README.round23.md +46 -0
  72. data/TODO.align/README.round24.md +53 -0
  73. data/TODO.align/README.round25.md +66 -0
  74. data/TODO.align/README.round26.md +55 -0
  75. data/TODO.align/README.round27.md +51 -0
  76. data/TODO.align/README.round28.md +61 -0
  77. data/TODO.align/README.round29.md +64 -0
  78. data/TODO.align/README.round30.md +50 -0
  79. data/TODO.align/README.round31.md +54 -0
  80. data/TODO.align/README.round32.md +67 -0
  81. data/TODO.align/README.round33.md +62 -0
  82. data/TODO.align/README.round34.md +70 -0
  83. data/docs/constraints.md +161 -0
  84. data/docs/generate_constraint_docs.rb +44 -0
  85. data/exe/chemicalml +6 -0
  86. data/lib/chemicalml/cli/base.rb +68 -0
  87. data/lib/chemicalml/cli/command.rb +43 -0
  88. data/lib/chemicalml/cli/constraints_command.rb +27 -0
  89. data/lib/chemicalml/cli/conventions_command.rb +14 -0
  90. data/lib/chemicalml/cli/dictionaries_command.rb +14 -0
  91. data/lib/chemicalml/cli/elements_command.rb +17 -0
  92. data/lib/chemicalml/cli/enums_command.rb +17 -0
  93. data/lib/chemicalml/cli/info_command.rb +77 -0
  94. data/lib/chemicalml/cli/inspect_command.rb +39 -0
  95. data/lib/chemicalml/cli/validate_command.rb +54 -0
  96. data/lib/chemicalml/cli.rb +43 -0
  97. data/lib/chemicalml/cml/base/abundance.rb +21 -5
  98. data/lib/chemicalml/cml/base/action.rb +44 -6
  99. data/lib/chemicalml/cml/base/action_list.rb +44 -8
  100. data/lib/chemicalml/cml/base/alternative.rb +37 -0
  101. data/lib/chemicalml/cml/base/amount.rb +15 -5
  102. data/lib/chemicalml/cml/base/angle.rb +34 -7
  103. data/lib/chemicalml/cml/base/annotation.rb +38 -0
  104. data/lib/chemicalml/cml/base/any_cml.rb +12 -5
  105. data/lib/chemicalml/cml/base/appinfo.rb +40 -0
  106. data/lib/chemicalml/cml/base/arg.rb +80 -0
  107. data/lib/chemicalml/cml/base/array.rb +51 -8
  108. data/lib/chemicalml/cml/base/array_list.rb +15 -5
  109. data/lib/chemicalml/cml/base/atom.rb +93 -19
  110. data/lib/chemicalml/cml/base/atom_array.rb +77 -2
  111. data/lib/chemicalml/cml/base/atom_parity.rb +18 -2
  112. data/lib/chemicalml/cml/base/atom_set.rb +15 -5
  113. data/lib/chemicalml/cml/base/atom_type.rb +27 -7
  114. data/lib/chemicalml/cml/base/atom_type_list.rb +23 -7
  115. data/lib/chemicalml/cml/base/atomic_basis_function.rb +35 -11
  116. data/lib/chemicalml/cml/base/band.rb +22 -7
  117. data/lib/chemicalml/cml/base/band_list.rb +15 -7
  118. data/lib/chemicalml/cml/base/basis_set.rb +26 -7
  119. data/lib/chemicalml/cml/base/bond.rb +39 -7
  120. data/lib/chemicalml/cml/base/bond_array.rb +41 -2
  121. data/lib/chemicalml/cml/base/bond_set.rb +15 -5
  122. data/lib/chemicalml/cml/base/bond_stereo.rb +25 -4
  123. data/lib/chemicalml/cml/base/bond_type.rb +24 -7
  124. data/lib/chemicalml/cml/base/bond_type_list.rb +23 -7
  125. data/lib/chemicalml/cml/base/cell_parameter.rb +30 -8
  126. data/lib/chemicalml/cml/base/cml_module.rb +36 -11
  127. data/lib/chemicalml/cml/base/common_children.rb +35 -0
  128. data/lib/chemicalml/cml/base/complex_object.rb +37 -0
  129. data/lib/chemicalml/cml/base/condition_list.rb +28 -8
  130. data/lib/chemicalml/cml/base/crystal.rb +27 -9
  131. data/lib/chemicalml/cml/base/definition.rb +12 -5
  132. data/lib/chemicalml/cml/base/description.rb +15 -5
  133. data/lib/chemicalml/cml/base/dictionary.rb +30 -7
  134. data/lib/chemicalml/cml/base/dictionary_entry.rb +67 -8
  135. data/lib/chemicalml/cml/base/dimension.rb +31 -7
  136. data/lib/chemicalml/cml/base/document.rb +18 -8
  137. data/lib/chemicalml/cml/base/documentation.rb +12 -5
  138. data/lib/chemicalml/cml/base/eigen.rb +33 -7
  139. data/lib/chemicalml/cml/base/electron.rb +29 -9
  140. data/lib/chemicalml/cml/base/enumeration.rb +41 -0
  141. data/lib/chemicalml/cml/base/expression.rb +47 -0
  142. data/lib/chemicalml/cml/base/float.rb +52 -0
  143. data/lib/chemicalml/cml/base/float_array.rb +55 -0
  144. data/lib/chemicalml/cml/base/formula.rb +31 -10
  145. data/lib/chemicalml/cml/base/fragment.rb +33 -9
  146. data/lib/chemicalml/cml/base/fragment_list.rb +26 -7
  147. data/lib/chemicalml/cml/base/gradient.rb +19 -6
  148. data/lib/chemicalml/cml/base/identifier.rb +22 -4
  149. data/lib/chemicalml/cml/base/integer.rb +52 -0
  150. data/lib/chemicalml/cml/base/integer_array.rb +55 -0
  151. data/lib/chemicalml/cml/base/isotope.rb +29 -8
  152. data/lib/chemicalml/cml/base/isotope_list.rb +18 -7
  153. data/lib/chemicalml/cml/base/join.rb +28 -6
  154. data/lib/chemicalml/cml/base/kpoint.rb +17 -6
  155. data/lib/chemicalml/cml/base/kpoint_list.rb +15 -7
  156. data/lib/chemicalml/cml/base/label.rb +15 -5
  157. data/lib/chemicalml/cml/base/lattice.rb +36 -9
  158. data/lib/chemicalml/cml/base/lattice_vector.rb +17 -6
  159. data/lib/chemicalml/cml/base/length.rb +31 -7
  160. data/lib/chemicalml/cml/base/line3.rb +21 -5
  161. data/lib/chemicalml/cml/base/link.rb +51 -10
  162. data/lib/chemicalml/cml/base/list.rb +23 -9
  163. data/lib/chemicalml/cml/base/map.rb +38 -5
  164. data/lib/chemicalml/cml/base/matrix.rb +38 -9
  165. data/lib/chemicalml/cml/base/mechanism.rb +20 -7
  166. data/lib/chemicalml/cml/base/mechanism_component.rb +14 -6
  167. data/lib/chemicalml/cml/base/metadata.rb +20 -6
  168. data/lib/chemicalml/cml/base/metadata_list.rb +30 -5
  169. data/lib/chemicalml/cml/base/molecule.rb +95 -21
  170. data/lib/chemicalml/cml/base/molecule_list.rb +27 -7
  171. data/lib/chemicalml/cml/base/name.rb +11 -3
  172. data/lib/chemicalml/cml/base/object.rb +20 -6
  173. data/lib/chemicalml/cml/base/observation.rb +18 -5
  174. data/lib/chemicalml/cml/base/operator.rb +40 -0
  175. data/lib/chemicalml/cml/base/parameter.rb +38 -8
  176. data/lib/chemicalml/cml/base/parameter_list.rb +30 -5
  177. data/lib/chemicalml/cml/base/particle.rb +23 -6
  178. data/lib/chemicalml/cml/base/peak.rb +86 -11
  179. data/lib/chemicalml/cml/base/peak_group.rb +81 -7
  180. data/lib/chemicalml/cml/base/peak_list.rb +25 -8
  181. data/lib/chemicalml/cml/base/peak_structure.rb +47 -6
  182. data/lib/chemicalml/cml/base/plane3.rb +15 -5
  183. data/lib/chemicalml/cml/base/point3.rb +15 -5
  184. data/lib/chemicalml/cml/base/potential.rb +17 -6
  185. data/lib/chemicalml/cml/base/potential_form.rb +31 -6
  186. data/lib/chemicalml/cml/base/potential_list.rb +15 -7
  187. data/lib/chemicalml/cml/base/product.rb +53 -2
  188. data/lib/chemicalml/cml/base/product_list.rb +36 -2
  189. data/lib/chemicalml/cml/base/property.rb +32 -8
  190. data/lib/chemicalml/cml/base/property_list.rb +26 -5
  191. data/lib/chemicalml/cml/base/reactant.rb +53 -2
  192. data/lib/chemicalml/cml/base/reactant_list.rb +36 -2
  193. data/lib/chemicalml/cml/base/reaction.rb +82 -12
  194. data/lib/chemicalml/cml/base/reaction_list.rb +29 -2
  195. data/lib/chemicalml/cml/base/reaction_scheme.rb +48 -8
  196. data/lib/chemicalml/cml/base/reaction_step.rb +33 -9
  197. data/lib/chemicalml/cml/base/reaction_step_list.rb +34 -7
  198. data/lib/chemicalml/cml/base/reactive_centre.rb +31 -6
  199. data/lib/chemicalml/cml/base/region.rb +24 -5
  200. data/lib/chemicalml/cml/base/related_entry.rb +34 -0
  201. data/lib/chemicalml/cml/base/sample.rb +32 -5
  202. data/lib/chemicalml/cml/base/scalar.rb +41 -6
  203. data/lib/chemicalml/cml/base/spectator.rb +26 -5
  204. data/lib/chemicalml/cml/base/spectator_list.rb +20 -7
  205. data/lib/chemicalml/cml/base/spectrum.rb +63 -12
  206. data/lib/chemicalml/cml/base/spectrum_data.rb +20 -8
  207. data/lib/chemicalml/cml/base/spectrum_list.rb +30 -7
  208. data/lib/chemicalml/cml/base/sphere3.rb +15 -5
  209. data/lib/chemicalml/cml/base/stmml.rb +12 -5
  210. data/lib/chemicalml/cml/base/string.rb +40 -0
  211. data/lib/chemicalml/cml/base/string_array.rb +52 -0
  212. data/lib/chemicalml/cml/base/substance.rb +42 -8
  213. data/lib/chemicalml/cml/base/substance_list.rb +32 -7
  214. data/lib/chemicalml/cml/base/symmetry.rb +33 -7
  215. data/lib/chemicalml/cml/base/system.rb +23 -8
  216. data/lib/chemicalml/cml/base/table.rb +43 -8
  217. data/lib/chemicalml/cml/base/table_cell.rb +12 -5
  218. data/lib/chemicalml/cml/base/table_content.rb +20 -8
  219. data/lib/chemicalml/cml/base/table_header.rb +15 -7
  220. data/lib/chemicalml/cml/base/table_header_cell.rb +27 -5
  221. data/lib/chemicalml/cml/base/table_row.rb +15 -7
  222. data/lib/chemicalml/cml/base/table_row_list.rb +15 -7
  223. data/lib/chemicalml/cml/base/tcell.rb +43 -0
  224. data/lib/chemicalml/cml/base/torsion.rb +34 -7
  225. data/lib/chemicalml/cml/base/transform3.rb +12 -5
  226. data/lib/chemicalml/cml/base/transition_state.rb +23 -5
  227. data/lib/chemicalml/cml/base/trow.rb +48 -0
  228. data/lib/chemicalml/cml/base/unit.rb +45 -10
  229. data/lib/chemicalml/cml/base/unit_list.rb +40 -6
  230. data/lib/chemicalml/cml/base/unit_type.rb +32 -5
  231. data/lib/chemicalml/cml/base/unit_type_list.rb +34 -6
  232. data/lib/chemicalml/cml/base/vector3.rb +15 -5
  233. data/lib/chemicalml/cml/base/xaxis.rb +33 -7
  234. data/lib/chemicalml/cml/base/yaxis.rb +33 -7
  235. data/lib/chemicalml/cml/base/z_matrix.rb +26 -5
  236. data/lib/chemicalml/cml/base.rb +139 -121
  237. data/lib/chemicalml/cml/canonical_comparison.rb +54 -0
  238. data/lib/chemicalml/cml/elements.rb +168 -124
  239. data/lib/chemicalml/cml/enums.rb +55 -0
  240. data/lib/chemicalml/cml/namespace.rb +2 -3
  241. data/lib/chemicalml/cml/patterns.rb +33 -0
  242. data/lib/chemicalml/cml/reference_resolver.rb +137 -0
  243. data/lib/chemicalml/cml/role/alternative.rb +10 -0
  244. data/lib/chemicalml/cml/role/annotation.rb +10 -0
  245. data/lib/chemicalml/cml/role/appinfo.rb +10 -0
  246. data/lib/chemicalml/cml/role/arg.rb +10 -0
  247. data/lib/chemicalml/cml/role/cml_module.rb +2 -3
  248. data/lib/chemicalml/cml/role/complex_object.rb +10 -0
  249. data/lib/chemicalml/cml/role/enumeration.rb +10 -0
  250. data/lib/chemicalml/cml/role/expression.rb +10 -0
  251. data/lib/chemicalml/cml/role/float.rb +10 -0
  252. data/lib/chemicalml/cml/role/float_array.rb +10 -0
  253. data/lib/chemicalml/cml/role/integer.rb +10 -0
  254. data/lib/chemicalml/cml/role/integer_array.rb +10 -0
  255. data/lib/chemicalml/cml/role/operator.rb +10 -0
  256. data/lib/chemicalml/cml/role/related_entry.rb +10 -0
  257. data/lib/chemicalml/cml/role/string.rb +10 -0
  258. data/lib/chemicalml/cml/role/string_array.rb +10 -0
  259. data/lib/chemicalml/cml/role/tcell.rb +10 -0
  260. data/lib/chemicalml/cml/role/trow.rb +10 -0
  261. data/lib/chemicalml/cml/role.rb +138 -121
  262. data/lib/chemicalml/cml/schema24/alternative.rb +14 -0
  263. data/lib/chemicalml/cml/schema24/annotation.rb +15 -0
  264. data/lib/chemicalml/cml/schema24/appinfo.rb +15 -0
  265. data/lib/chemicalml/cml/schema24/arg.rb +14 -0
  266. data/lib/chemicalml/cml/schema24/cml_module.rb +18 -0
  267. data/lib/chemicalml/cml/schema24/complex_object.rb +14 -0
  268. data/lib/chemicalml/cml/schema24/configuration.rb +15 -2
  269. data/lib/chemicalml/cml/schema24/enumeration.rb +16 -0
  270. data/lib/chemicalml/cml/schema24/expression.rb +14 -0
  271. data/lib/chemicalml/cml/schema24/float.rb +14 -0
  272. data/lib/chemicalml/cml/schema24/float_array.rb +14 -0
  273. data/lib/chemicalml/cml/schema24/integer.rb +14 -0
  274. data/lib/chemicalml/cml/schema24/integer_array.rb +14 -0
  275. data/lib/chemicalml/cml/schema24/operator.rb +14 -0
  276. data/lib/chemicalml/cml/schema24/related_entry.rb +14 -0
  277. data/lib/chemicalml/cml/schema24/string.rb +14 -0
  278. data/lib/chemicalml/cml/schema24/string_array.rb +14 -0
  279. data/lib/chemicalml/cml/schema24/tcell.rb +14 -0
  280. data/lib/chemicalml/cml/schema24/trow.rb +14 -0
  281. data/lib/chemicalml/cml/schema24.rb +154 -126
  282. data/lib/chemicalml/cml/schema3.rb +123 -123
  283. data/lib/chemicalml/cml/visitable.rb +77 -3
  284. data/lib/chemicalml/cml/wire_class_registry.rb +3 -3
  285. data/lib/chemicalml/cml.rb +164 -132
  286. data/lib/chemicalml/context_configuration.rb +12 -4
  287. data/lib/chemicalml/convention/base.rb +17 -3
  288. data/lib/chemicalml/convention/cascade/constraints/reaction_scheme_must_have_content.rb +33 -0
  289. data/lib/chemicalml/convention/cascade/constraints/reaction_step_list_must_contain_steps.rb +31 -0
  290. data/lib/chemicalml/convention/cascade/constraints/reaction_step_must_have_reaction_or_lists.rb +33 -0
  291. data/lib/chemicalml/convention/cascade/constraints/reactive_centre_atom_refs_should_be_present.rb +34 -0
  292. data/lib/chemicalml/convention/cascade/constraints.rb +18 -0
  293. data/lib/chemicalml/convention/cascade.rb +43 -0
  294. data/lib/chemicalml/convention/compchem/constraints/array_rules.rb +44 -0
  295. data/lib/chemicalml/convention/compchem/constraints/calculation_requires_finalization.rb +28 -0
  296. data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +5 -4
  297. data/lib/chemicalml/convention/compchem/constraints/environment_at_most_one_property_list.rb +26 -0
  298. data/lib/chemicalml/convention/compchem/constraints/environment_must_not_contain_parameter.rb +28 -0
  299. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_molecule.rb +26 -0
  300. data/lib/chemicalml/convention/compchem/constraints/finalization_at_most_one_property_list.rb +26 -0
  301. data/lib/chemicalml/convention/compchem/constraints/finalization_must_have_content.rb +44 -0
  302. data/lib/chemicalml/convention/compchem/constraints/finalization_must_not_contain_parameter.rb +27 -0
  303. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_molecule.rb +26 -0
  304. data/lib/chemicalml/convention/compchem/constraints/initialization_at_most_one_parameter_list.rb +26 -0
  305. data/lib/chemicalml/convention/compchem/constraints/initialization_must_have_content.rb +54 -0
  306. data/lib/chemicalml/convention/compchem/constraints/initialization_must_not_contain_property.rb +28 -0
  307. data/lib/chemicalml/convention/compchem/constraints/job_list_module_must_have_id.rb +25 -0
  308. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_environment.rb +25 -0
  309. data/lib/chemicalml/convention/compchem/constraints/job_module_at_most_one_finalization.rb +25 -0
  310. data/lib/chemicalml/convention/compchem/constraints/job_module_must_have_id.rb +25 -0
  311. data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +7 -9
  312. data/lib/chemicalml/convention/compchem/constraints/matrix_rules.rb +48 -0
  313. data/lib/chemicalml/convention/compchem/constraints/module_predicates.rb +50 -0
  314. data/lib/chemicalml/convention/compchem/constraints/scalar_units.rb +42 -0
  315. data/lib/chemicalml/convention/compchem/constraints.rb +40 -2
  316. data/lib/chemicalml/convention/compchem.rb +20 -2
  317. data/lib/chemicalml/convention/constraint.rb +68 -24
  318. data/lib/chemicalml/convention/coordinator.rb +90 -0
  319. data/lib/chemicalml/convention/detection.rb +39 -0
  320. data/lib/chemicalml/convention/dictionary/constraints/dictionary_must_have_namespace.rb +29 -0
  321. data/lib/chemicalml/convention/dictionary/constraints/dictionary_namespace_should_end_with_slash_or_hash.rb +32 -0
  322. data/lib/chemicalml/convention/dictionary/constraints/entry_id_must_match_pattern.rb +33 -0
  323. data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +4 -4
  324. data/lib/chemicalml/convention/dictionary/constraints/entry_must_contain_definition.rb +29 -0
  325. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +6 -6
  326. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_unit_type.rb +31 -0
  327. data/lib/chemicalml/convention/dictionary/constraints/entry_units_co_constraints.rb +44 -0
  328. data/lib/chemicalml/convention/dictionary/constraints.rb +14 -2
  329. data/lib/chemicalml/convention/dictionary.rb +8 -2
  330. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_be_child_of_molecule_or_formula.rb +41 -0
  331. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +18 -9
  332. data/lib/chemicalml/convention/molecular/constraints/atom_coordinates_must_be_paired.rb +6 -6
  333. data/lib/chemicalml/convention/molecular/constraints/atom_element_type_should_be_in_periodic_table.rb +39 -0
  334. data/lib/chemicalml/convention/molecular/constraints/atom_id_must_match_pattern.rb +7 -6
  335. data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +3 -2
  336. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_element_type.rb +4 -4
  337. data/lib/chemicalml/convention/molecular/constraints/atom_must_have_id.rb +5 -4
  338. data/lib/chemicalml/convention/molecular/constraints/atom_parity_atom_refs4_should_be_distinct.rb +44 -0
  339. data/lib/chemicalml/convention/molecular/constraints/atom_parity_should_include_parent_atom.rb +65 -0
  340. data/lib/chemicalml/convention/molecular/constraints/bond_array_must_be_child_of_molecule.rb +37 -0
  341. data/lib/chemicalml/convention/molecular/constraints/bond_atom_refs2_should_be_distinct.rb +44 -0
  342. data/lib/chemicalml/convention/molecular/constraints/bond_id_should_match_pattern.rb +38 -0
  343. data/lib/chemicalml/convention/molecular/constraints/bond_ids_unique_within_molecule.rb +44 -0
  344. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_atom_refs2.rb +4 -4
  345. data/lib/chemicalml/convention/molecular/constraints/bond_must_have_order.rb +5 -4
  346. data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +4 -3
  347. data/lib/chemicalml/convention/molecular/constraints/bond_order_other_must_have_dict_ref.rb +34 -0
  348. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_be_in_enum.rb +39 -0
  349. data/lib/chemicalml/convention/molecular/constraints/bond_order_should_not_be_numeric.rb +3 -3
  350. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_atom_refs4_should_be_distinct.rb +43 -0
  351. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_cis_trans_must_have_atom_refs4.rb +45 -0
  352. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_other_must_have_dict_ref.rb +33 -0
  353. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_should_be_in_enum.rb +38 -0
  354. data/lib/chemicalml/convention/molecular/constraints/bond_stereo_wedge_hash_must_have_atom_refs2.rb +45 -0
  355. data/lib/chemicalml/convention/molecular/constraints/dict_ref_should_resolve.rb +59 -0
  356. data/lib/chemicalml/convention/molecular/constraints/molecule_atom_array_mutually_exclusive_with_children.rb +33 -0
  357. data/lib/chemicalml/convention/molecular/constraints/molecule_bond_array_mutually_exclusive_with_children.rb +32 -0
  358. data/lib/chemicalml/convention/molecular/constraints/molecule_chirality_should_be_in_enum.rb +38 -0
  359. data/lib/chemicalml/convention/molecular/constraints/molecule_count_must_not_appear_on_top_level.rb +35 -0
  360. data/lib/chemicalml/convention/molecular/constraints/molecule_id_should_match_pattern.rb +38 -0
  361. data/lib/chemicalml/convention/molecular/constraints/molecule_must_have_id.rb +4 -4
  362. data/lib/chemicalml/convention/molecular/constraints/property_must_have_dict_ref.rb +4 -4
  363. data/lib/chemicalml/convention/molecular/constraints/property_scalar_data_type_matches_dictionary.rb +58 -0
  364. data/lib/chemicalml/convention/molecular/constraints/references_should_resolve.rb +37 -0
  365. data/lib/chemicalml/convention/molecular/constraints/scalar_must_have_data_type.rb +4 -4
  366. data/lib/chemicalml/convention/molecular/constraints.rb +59 -13
  367. data/lib/chemicalml/convention/molecular.rb +25 -2
  368. data/lib/chemicalml/convention/registry.rb +95 -10
  369. data/lib/chemicalml/convention/simple_unit/constraints/root_must_be_unit_list.rb +24 -0
  370. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_power.rb +32 -0
  371. data/lib/chemicalml/convention/simple_unit/constraints/unit_must_have_symbol.rb +31 -0
  372. data/lib/chemicalml/convention/simple_unit/constraints.rb +16 -0
  373. data/lib/chemicalml/convention/simple_unit.rb +37 -0
  374. data/lib/chemicalml/convention/spectroscopy/constraints/peak_list_must_contain_peaks.rb +31 -0
  375. data/lib/chemicalml/convention/spectroscopy/constraints/peak_should_have_values.rb +34 -0
  376. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_content.rb +31 -0
  377. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_convention.rb +32 -0
  378. data/lib/chemicalml/convention/spectroscopy/constraints/spectrum_must_have_format.rb +31 -0
  379. data/lib/chemicalml/convention/spectroscopy/constraints.rb +20 -0
  380. data/lib/chemicalml/convention/spectroscopy.rb +41 -0
  381. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_contain_at_least_one_unit.rb +29 -0
  382. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_list_must_have_namespace.rb +36 -0
  383. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_contain_definition.rb +28 -0
  384. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_id.rb +28 -0
  385. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_multiplier_or_constant_to_si.rb +34 -0
  386. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_parent_si.rb +32 -0
  387. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +4 -4
  388. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_title.rb +32 -0
  389. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_parent_si_should_resolve.rb +45 -0
  390. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_unit_type_should_resolve.rb +46 -0
  391. data/lib/chemicalml/convention/unit_dictionary/constraints.rb +19 -1
  392. data/lib/chemicalml/convention/unit_dictionary.rb +11 -2
  393. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_id_must_match_pattern.rb +31 -0
  394. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_contain_at_least_one_unit_type.rb +28 -0
  395. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_list_must_have_namespace.rb +35 -0
  396. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_contain_definition.rb +28 -0
  397. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +6 -6
  398. data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +9 -1
  399. data/lib/chemicalml/convention/unit_type_dictionary.rb +6 -2
  400. data/lib/chemicalml/convention/validation_report.rb +90 -0
  401. data/lib/chemicalml/convention/violation.rb +9 -3
  402. data/lib/chemicalml/convention.rb +49 -11
  403. data/lib/chemicalml/dictionary/entry.rb +6 -6
  404. data/lib/chemicalml/dictionary/enum.rb +1 -1
  405. data/lib/chemicalml/dictionary/link.rb +3 -3
  406. data/lib/chemicalml/dictionary/loader.rb +19 -19
  407. data/lib/chemicalml/dictionary/model.rb +5 -5
  408. data/lib/chemicalml/dictionary/registry.rb +9 -9
  409. data/lib/chemicalml/dictionary.rb +13 -9
  410. data/lib/chemicalml/logger.rb +47 -0
  411. data/lib/chemicalml/schema/definition.rb +2 -2
  412. data/lib/chemicalml/schema.rb +7 -7
  413. data/lib/chemicalml/version.rb +1 -1
  414. data/lib/chemicalml/versioned_parser.rb +28 -10
  415. data/lib/chemicalml.rb +79 -17
  416. data/lib/tasks/reference_fetch.rake +30 -31
  417. data/lib/tasks/scrape_fixtures.rake +23 -24
  418. metadata +235 -35
  419. data/lib/chemicalml/cml/child_mappings.rb +0 -168
  420. data/lib/chemicalml/cml/translator/value_translations.rb +0 -270
  421. data/lib/chemicalml/cml/translator.rb +0 -484
  422. data/lib/chemicalml/model/atom.rb +0 -64
  423. data/lib/chemicalml/model/atom_parity.rb +0 -21
  424. data/lib/chemicalml/model/bond.rb +0 -39
  425. data/lib/chemicalml/model/bond_stereo.rb +0 -29
  426. data/lib/chemicalml/model/cml_array.rb +0 -27
  427. data/lib/chemicalml/model/cml_module.rb +0 -45
  428. data/lib/chemicalml/model/document.rb +0 -29
  429. data/lib/chemicalml/model/formula.rb +0 -34
  430. data/lib/chemicalml/model/identifier.rb +0 -21
  431. data/lib/chemicalml/model/label.rb +0 -21
  432. data/lib/chemicalml/model/matrix.rb +0 -29
  433. data/lib/chemicalml/model/metadata.rb +0 -23
  434. data/lib/chemicalml/model/metadata_list.rb +0 -25
  435. data/lib/chemicalml/model/molecule.rb +0 -45
  436. data/lib/chemicalml/model/name.rb +0 -21
  437. data/lib/chemicalml/model/node.rb +0 -45
  438. data/lib/chemicalml/model/parameter.rb +0 -27
  439. data/lib/chemicalml/model/parameter_list.rb +0 -25
  440. data/lib/chemicalml/model/product.rb +0 -21
  441. data/lib/chemicalml/model/product_list.rb +0 -21
  442. data/lib/chemicalml/model/property.rb +0 -28
  443. data/lib/chemicalml/model/property_list.rb +0 -25
  444. data/lib/chemicalml/model/reactant.rb +0 -21
  445. data/lib/chemicalml/model/reactant_list.rb +0 -21
  446. data/lib/chemicalml/model/reaction.rb +0 -41
  447. data/lib/chemicalml/model/reaction_list.rb +0 -23
  448. data/lib/chemicalml/model/scalar.rb +0 -25
  449. data/lib/chemicalml/model/substance.rb +0 -25
  450. data/lib/chemicalml/model.rb +0 -40
@@ -0,0 +1,31 @@
1
+ # 77 — Convention coverage matrix spec
2
+
3
+ ## Why
4
+
5
+ The gem registers 8 conventions, each with multiple constraints.
6
+ Currently each convention has its own spec file, but there is no
7
+ single source of truth asserting that every convention:
8
+
9
+ - is in the Registry
10
+ - is detectable via at least one root role
11
+ - has at least one violation case for at least one of its constraints
12
+ - has at least one passing-case example
13
+
14
+ A matrix spec catches gaps when adding a new convention (the new
15
+ convention's matrix entry fails until it has specs).
16
+
17
+ ## Work
18
+
19
+ Add `spec/chemicalml/convention/coverage_matrix_spec.rb` that:
20
+
21
+ 1. Iterates `Convention::Registry.builtin_qnames`.
22
+ 2. For each, asserts the convention is detectable from its declared
23
+ root role.
24
+ 3. For each constraint class registered against the convention,
25
+ asserts a spec file exists that exercises it (grep for the class
26
+ name in `spec/`).
27
+
28
+ ## Acceptance
29
+
30
+ - Matrix spec passes for all 8 conventions.
31
+ - Adding a 9th convention without specs makes the matrix spec fail.
@@ -0,0 +1,23 @@
1
+ # 78 — Fixture coverage audit
2
+
3
+ ## Why
4
+
5
+ `spec/fixtures/` holds CML examples. Current round-trip specs cover a
6
+ subset. After recent additions (parallel-array attrs, CommonChildren,
7
+ 8 conventions), some features may have no fixture exercising them.
8
+
9
+ ## Work
10
+
11
+ Audit `spec/fixtures/`:
12
+
13
+ 1. Identify fixtures that exercise parallel-array atomArray/bondArray.
14
+ 2. Identify fixtures covering each convention.
15
+ 3. Identify fixtures using new child elements (reaction.mechanism,
16
+ reactiveCentre.atomSet, etc.).
17
+ 4. For features with no fixture, add one under `spec/fixtures/`.
18
+
19
+ ## Acceptance
20
+
21
+ - Every convention has at least one fixture.
22
+ - Parallel-array form has at least one fixture.
23
+ - Schema24-only legacy elements have at least one fixture.
@@ -0,0 +1,15 @@
1
+ # 79 — Final verification round 19
2
+
3
+ ## Work
4
+
5
+ 1. Full rspec — must be green (or pending for documented limitations).
6
+ 2. rubocop on touched files.
7
+ 3. Forbidden-pattern scan.
8
+ 4. Update CLAUDE.md noting key_value support and JSON wire names.
9
+ 5. Write `TODO.align/README.round19.md`.
10
+
11
+ ## Acceptance
12
+
13
+ - All four scans clean.
14
+ - CLAUDE.md current.
15
+ - README.round19.md exists.
@@ -0,0 +1,35 @@
1
+ # 80 — XSD enum constants
2
+
3
+ ## Why
4
+
5
+ The XSD declares ~30 simpleTypes that restrict to enumeration sets
6
+ (orderType, stereoType, chiralityType, latticeType, matrixType,
7
+ stateType, peakMultiplicityType, etc.). We model every attribute as
8
+ `:string` and accept any value.
9
+
10
+ Without a single source of truth for the allowed values, downstream
11
+ callers that want to validate CML have to scrape the XSD themselves
12
+ or hard-code the enums (which drift from the schema).
13
+
14
+ ## Solution
15
+
16
+ `Chemicalml::Cml::Enums` — frozen-set constants for each XSD enum
17
+ simpleType. The constants are the canonical Ruby source of truth
18
+ for "what values does this attribute accept".
19
+
20
+ ## Work
21
+
22
+ 1. Create `lib/chemicalml/cml/enums.rb` with one constant per XSD
23
+ enum simpleType. Names match the XSD type name with `Type`
24
+ suffix stripped (e.g. `ORDER_VALUES` for `orderType`).
25
+ 2. Each constant is a frozen Set of allowed string values.
26
+ 3. Add specs asserting the constants match the XSD exactly.
27
+
28
+ ## Acceptance
29
+
30
+ - `Chemicalml::Cml::Enums::ORDER_VALUES` includes `"S"`, `"D"`, `"T"`,
31
+ `"A"`, etc.
32
+ - `Chemicalml::Cml::Enums::STEREO_VALUES` includes `"C"`, `"T"`, `"W"`,
33
+ `"H"`.
34
+ - Constants are frozen.
35
+ - Generated from the XSD (no hand-maintenance).
@@ -0,0 +1,29 @@
1
+ # 81 — Enum validation constraints
2
+
3
+ ## Why
4
+
5
+ With enum constants defined (TODO 80), add warning-severity
6
+ constraints that flag attribute values outside the XSD-declared
7
+ enum set. Warning (not error) because CML allows extension values.
8
+
9
+ ## Work
10
+
11
+ Add constraints under the molecular convention (where most enum
12
+ attributes live):
13
+
14
+ 1. `BondOrderShouldBeInEnum` (applies_to Role::Bond) — warning if
15
+ `bond.order` not in `Cml::Enums::ORDER_VALUES`.
16
+ 2. `BondStereoShouldBeInEnum` (applies_to Role::BondStereo) —
17
+ warning if `bondStereo.value` not in `Cml::Enums::STEREO_VALUES`.
18
+ 3. `MoleculeChiralityShouldBeInEnum` (applies_to Role::Molecule) —
19
+ warning if `molecule.chirality` not in `Cml::Enums::CHIRALITY_VALUES`.
20
+ 4. `LatticeTypeShouldBeInEnum` (applies_to Role::Lattice) —
21
+ warning if not in `LATTICE_VALUES`.
22
+
23
+ Each constraint is registered against the molecular convention.
24
+
25
+ ## Acceptance
26
+
27
+ - A bond with `order="X"` triggers a warning.
28
+ - A bond with `order="S"` passes silently.
29
+ - All existing specs pass.
@@ -0,0 +1,33 @@
1
+ # 82 — Reference resolver
2
+
3
+ ## Why
4
+
5
+ CML documents reference atoms/bonds/molecules by id via attributes
6
+ like `atomRefs2`, `atomRefs4`, `bondRefs`, `moleculeRefs`, `ref`.
7
+ Currently callers must walk the document themselves to resolve
8
+ these to actual instances.
9
+
10
+ ## Solution
11
+
12
+ `Chemicalml::Cml::ReferenceResolver` — walks a document, builds an
13
+ id → node index, then exposes lookup methods.
14
+
15
+ ## Work
16
+
17
+ 1. Create `lib/chemicalml/cml/reference_resolver.rb`.
18
+ 2. API:
19
+ ```ruby
20
+ resolver = Chemicalml::Cml::ReferenceResolver.new(document)
21
+ resolver.find_atom(molecule, "a1") # → Atom or nil
22
+ resolver.resolve_atom_refs2(bond) # → [Atom, Atom] or nils
23
+ resolver.unresolved_refs(document) # → [{ source: Bond, attr: :atom_refs2, missing: ["a99"] }]
24
+ ```
25
+ 3. Add a constraint `ReferencesShouldResolve` (warning severity)
26
+ that lists unresolved refs in a document.
27
+ 4. Specs covering resolve + unresolved detection.
28
+
29
+ ## Acceptance
30
+
31
+ - Resolver resolves a valid atomRefs2 to actual atoms.
32
+ - `unresolved_refs` returns missing atom ids.
33
+ - All existing specs pass.
@@ -0,0 +1,20 @@
1
+ # 83 — TOML round-trip spec
2
+
3
+ ## Why
4
+
5
+ lutaml-model supports TOML as a key-value format. CLAUDE.md claims
6
+ format-agnosticism — XML/JSON/YAML are tested. Add TOML to complete
7
+ the set.
8
+
9
+ ## Work
10
+
11
+ Add `spec/chemicalml/cml/toml_roundtrip_spec.rb` that round-trips
12
+ a Molecule through `to_toml` / `from_toml`. TOML's flat key/value
13
+ structure means nested objects get flattened — verify the same
14
+ data survives.
15
+
16
+ ## Acceptance
17
+
18
+ - `Molecule#to_toml` produces a TOML string.
19
+ - `Molecule.from_toml(toml)` returns a Molecule with the same id
20
+ and atom data.
@@ -0,0 +1,16 @@
1
+ # 84 — Final verification round 21
2
+
3
+ ## Work
4
+
5
+ 1. Full rspec — must be green.
6
+ 2. rubocop on touched files.
7
+ 3. Forbidden-pattern scan.
8
+ 4. Update CLAUDE.md noting enum validation, reference resolver,
9
+ TOML round-trip.
10
+ 5. Write `TODO.align/README.round21.md`.
11
+
12
+ ## Acceptance
13
+
14
+ - All scans clean.
15
+ - CLAUDE.md current.
16
+ - README.round21.md exists.
@@ -0,0 +1,25 @@
1
+ # 85 — Bond atomRefs2 distinct constraint
2
+
3
+ ## Why
4
+
5
+ The molecular spec at `reference-docs/conventions/molecular.md` says:
6
+
7
+ > `atomRefs2` — REQUIRED, two distinct atom ids in same molecule
8
+
9
+ No current constraint enforces distinctness. A `<bond atomRefs2="a1 a1"/>`
10
+ (zero-length self-bond) is silently accepted.
11
+
12
+ ## Work
13
+
14
+ Add `Molecular::Constraints::BondAtomRefs2ShouldBeDistinct` (warning
15
+ severity — there are edge cases in non-classical chemistry). The
16
+ constraint parses `node.atom_refs2`, splits on whitespace, and warns
17
+ if the two ids are identical.
18
+
19
+ Register against molecular convention.
20
+
21
+ ## Acceptance
22
+
23
+ - `<bond atomRefs2="a1 a1"/>` triggers a warning.
24
+ - `<bond atomRefs2="a1 a2"/>` passes silently.
25
+ - All existing specs pass.
@@ -0,0 +1,20 @@
1
+ # 86 — References-should-resolve constraint
2
+
3
+ ## Why
4
+
5
+ With `Cml::ReferenceResolver` (TODO 82), we can now detect bonds
6
+ whose `atomRefs2` reference atoms that don't exist in the parent
7
+ molecule. Currently no constraint flags this — typos like
8
+ `atomRefs2="a1 a99"` (where `a99` doesn't exist) pass silently.
9
+
10
+ ## Work
11
+
12
+ Add `Molecular::Constraints::ReferencesShouldResolve` as a
13
+ `DocumentConstraint`. It instantiates a `ReferenceResolver` and
14
+ reports each unresolved reference as a warning violation.
15
+
16
+ ## Acceptance
17
+
18
+ - A bond referencing a missing atom triggers a warning.
19
+ - A bond referencing existing atoms passes silently.
20
+ - All existing specs pass.
@@ -0,0 +1,26 @@
1
+ # 87 — CLI utility for validation
2
+
3
+ ## Why
4
+
5
+ A user-facing CLI makes the gem immediately useful for command-line
6
+ validation of CML files. Without a CLI, users have to write Ruby
7
+ to use the gem.
8
+
9
+ ## Work
10
+
11
+ Create `exe/chemicalml` (executable) with subcommands:
12
+
13
+ - `chemicalml validate <file>` — auto-detect convention, print
14
+ violations to stderr, exit non-zero if errors.
15
+ - `chemicalml conventions` — list registered conventions.
16
+ - `chemicalml dictionaries` — list builtin dictionaries.
17
+
18
+ Wire it through `Chemicalml::Cli` module under
19
+ `lib/chemicalml/cli.rb` so the logic is reusable.
20
+
21
+ ## Acceptance
22
+
23
+ - `bundle exec chemicalml validate spec/fixtures/schema3/molecular/water.cml`
24
+ parses, detects convention:molecular, prints nothing, exits 0.
25
+ - `chemicalml conventions` lists all 8 conventions.
26
+ - `chemicalml dictionaries` lists all built-in dictionaries.
@@ -0,0 +1,26 @@
1
+ # 88 — Diverse real-world fixtures
2
+
3
+ ## Why
4
+
5
+ Current fixtures cover basic structures (water, methanol, ethanol,
6
+ compchem, dictionary). Real CML documents have richer features:
7
+ bond stereo (wedge/hash), propertyList nesting, reaction mechanisms,
8
+ formulas, crystallographic lattices. Adding fixtures that exercise
9
+ these ensures the gem handles real-world CML.
10
+
11
+ ## Work
12
+
13
+ Add fixtures under `spec/fixtures/schema3/`:
14
+ - `molecular/chiral_center_with_bond_stereo.cml` — bondStereo W/H
15
+ - `molecular/ethanol_with_properties.cml` — propertyList with scalars
16
+ - `molecular/ethanol_with_formula.cml` — formula concise
17
+ - `crystal/nacl_with_lattice.cml` — crystal + lattice + symmetry
18
+ - `reactions/diels_alder.cml` — reactionScheme with reactantList/productList
19
+
20
+ Round-trip each in a spec.
21
+
22
+ ## Acceptance
23
+
24
+ - Each fixture parses without raising.
25
+ - The features it exercises (bondStereo, property, formula, lattice,
26
+ reaction) round-trip.
@@ -0,0 +1,15 @@
1
+ # 89 — Final verification round 22
2
+
3
+ ## Work
4
+
5
+ 1. Full rspec — must be green.
6
+ 2. rubocop on touched files.
7
+ 3. Forbidden-pattern scan.
8
+ 4. Update CLAUDE.md noting CLI + new constraints.
9
+ 5. Write `TODO.align/README.round22.md`.
10
+
11
+ ## Acceptance
12
+
13
+ - All scans clean.
14
+ - CLAUDE.md current.
15
+ - README.round22.md exists.
@@ -0,0 +1,31 @@
1
+ # 90 — BondStereo completeness constraints
2
+
3
+ ## Why
4
+
5
+ The molecular spec at `reference-docs/conventions/molecular.md` says:
6
+
7
+ > If value is `C` or `T` — MUST have `atomRefs4` (four distinct atom
8
+ > ids; two MUST be the parent bond's atoms). MUST NOT have `atomRefs2`.
9
+ > If value is `W` or `H` — MUST have `atomRefs2` (two distinct atom
10
+ > ids ... both MUST be in the parent bond).
11
+
12
+ We have `BondStereoCisTransMustHaveAtomRefs4` and
13
+ `BondStereoWedgeHashMustHaveAtomRefs2` but not the distinctness or
14
+ parent-bond-membership rules.
15
+
16
+ ## Work
17
+
18
+ Add to molecular convention:
19
+ - `BondStereoAtomRefs4ShouldBeDistinct` (warning) — atomRefs4 has 4 distinct ids
20
+ - `BondStereoAtomsShouldBeInParentBond` (warning) — atomRefs2/4
21
+ references should be a subset of the parent bond's atoms (the
22
+ walker doesn't track parents, so this is approximate — checks
23
+ against the document's atoms).
24
+
25
+ Register both against molecular.
26
+
27
+ ## Acceptance
28
+
29
+ - `<bondStereo atomRefs4="a1 a2 a3 a3">C</bondStereo>` triggers warning.
30
+ - `<bondStereo atomRefs4="a1 a2 a3 a4">C</bondStereo>` passes.
31
+ - All existing specs pass.
@@ -0,0 +1,21 @@
1
+ # 91 — AtomParity atomRefs4 distinct constraint
2
+
3
+ ## Why
4
+
5
+ `<atomParity atomRefs4="a b c d">1</atomParity>` references 4 atoms
6
+ around a chiral center. Per CML semantics, all 4 must be distinct
7
+ (duplicates make the parity meaningless). No constraint enforces
8
+ this today.
9
+
10
+ ## Work
11
+
12
+ Add `Molecular::Constraints::AtomParityAtomRefs4ShouldBeDistinct`
13
+ (applies_to Role::AtomParity). Warning severity. Parses
14
+ `node.atom_refs4` and warns if not 4 distinct ids.
15
+
16
+ Register against molecular.
17
+
18
+ ## Acceptance
19
+
20
+ - AtomParity with `atomRefs4="a1 a2 a3 a3"` triggers warning.
21
+ - AtomParity with `atomRefs4="a1 a2 a3 a4"` passes.
@@ -0,0 +1,28 @@
1
+ # 92 — Unit-dictionary completeness
2
+
3
+ ## Why
4
+
5
+ `reference-docs/conventions/unit-dictionary.md` says unit MUST have:
6
+ - `id` (already covered)
7
+ - `title` — typically the full name
8
+ - `symbol` (already covered alongside unitType)
9
+ - `parentSI` — QName referencing an SI unit
10
+ - at least one of `multiplierToSI` or `constantToSI`
11
+ - `unitType` (already covered)
12
+ - single `definition` child (already covered)
13
+
14
+ Missing constraints: title, parentSI, multiplierTo/constantToSI.
15
+
16
+ ## Work
17
+
18
+ Add three constraints to unit-dictionary convention:
19
+ - `UnitMustHaveTitle`
20
+ - `UnitMustHaveParentSI`
21
+ - `UnitMustHaveMultiplierOrConstantToSI`
22
+
23
+ ## Acceptance
24
+
25
+ - A `<unit>` without `title` triggers an error.
26
+ - A `<unit>` with `parentSI="si:m"` passes.
27
+ - A `<unit>` without both `multiplierToSI` and `constantToSI` triggers error.
28
+ - All existing specs pass.
@@ -0,0 +1,23 @@
1
+ # 93 — YARD docs for public API
2
+
3
+ ## Why
4
+
5
+ The public API (`Chemicalml.parse`, `Chemicalml.serialize`,
6
+ `Chemicalml::Cli.run`, `Chemicalml::Convention.validate`,
7
+ `Chemicalml::Dictionary.load`) lacks YARD docstrings. Without
8
+ docs, `yard doc` produces empty output and users have to read source.
9
+
10
+ ## Work
11
+
12
+ Add concise YARD docstrings to:
13
+ - `Chemicalml.parse`, `Chemicalml.serialize`, `Chemicalml.parser_for`
14
+ - `Chemicalml::Cli.run` and subcommand methods
15
+ - `Chemicalml::Convention.validate`, `validate_report`, `detect_and_validate`
16
+ - `Chemicalml::Dictionary.load`
17
+ - `Chemicalml::Cml::ReferenceResolver` public methods
18
+ - `Chemicalml::Cml::Enums` module
19
+
20
+ ## Acceptance
21
+
22
+ - `bundle exec yard doc` succeeds with no errors.
23
+ - `bundle exec yard server` serves documented API.
@@ -0,0 +1,15 @@
1
+ # 94 — Final verification round 23
2
+
3
+ ## Work
4
+
5
+ 1. Full rspec — must be green.
6
+ 2. rubocop on touched files.
7
+ 3. Forbidden-pattern scan.
8
+ 4. Update CLAUDE.md noting new constraints + YARD.
9
+ 5. Write `TODO.align/README.round23.md`.
10
+
11
+ ## Acceptance
12
+
13
+ - All scans clean.
14
+ - CLAUDE.md current.
15
+ - README.round23.md exists.
@@ -0,0 +1,28 @@
1
+ # Round 17 — XSD gap closure
2
+
3
+ This round closes the remaining XSD-declared feature gaps in the gem.
4
+ Started from a clean XSD-vs-Ruby comparison and worked through each
5
+ cluster.
6
+
7
+ ## Files
8
+
9
+ - [61 — Parallel-array attrs for atomArray/bondArray](61-parallel-array-attrs.md)
10
+ - [62 — Per-element attribute gaps](62-per-element-attribute-gaps.md)
11
+ - [63 — Universal children mixin](63-universal-children-mixin.md)
12
+ - [64 — Per-element child element gaps (Schema 2.4)](64-per-element-child-gaps.md)
13
+ - [65 — SimpleUnit convention](65-simple-unit-convention.md)
14
+ - [66 — Schema24-only legacy element children](66-schema24-legacy-children.md)
15
+ - [67 — Final spec/lint/docs verification](67-final-verification.md)
16
+
17
+ ## Outcomes
18
+
19
+ - Schema3 XSD: **0 attribute gaps, 0 child gaps** (excluding `<anyCml>`
20
+ wildcard, which is intentionally not modelled as a concrete child).
21
+ - Schema24 XSD: **0 attribute gaps**; child gaps reduced from 235 to
22
+ ~140 element-specific declarations, with ~80% of the universal
23
+ children (metadataList/label/name/description) absorbed by the new
24
+ `CommonChildren` mixin.
25
+ - 8 conventions now registered (was 5 at start of round): molecular,
26
+ compchem, dictionary, unit-dictionary, unitType-dictionary,
27
+ spectroscopy, cascade, simpleUnit.
28
+ - 300 specs total, 0 failures, 0 forbidden patterns.
@@ -0,0 +1,37 @@
1
+ # Round 18 — Convention detection, format coverage, robustness
2
+
3
+ This round closes convention-detection gaps, proves format-agnosticism
4
+ beyond XML, hardens the constraint walker, and enriches the violation
5
+ value-object.
6
+
7
+ ## Files
8
+
9
+ - [68 — Detection coverage for all 8 conventions](68-detection-coverage.md)
10
+ - [69 — JSON / YAML round-trip proof](69-json-yaml-roundtrip.md)
11
+ - [70 — Iterative tree walker](70-iterative-walker.md)
12
+ - [71 — Enrich Violation with offending value](71-violation-value.md)
13
+ - [72 — Close remaining Schema24 child gaps](72-close-child-gaps.md)
14
+ - [73 — Document Schema24 nested-parse limitation](73-schema24-parse-limitation.md)
15
+ - [74 — Final verification round 18](74-final-verification-round18.md)
16
+
17
+ ## Outcomes
18
+
19
+ - `Convention::Detection` recognises Spectrum, SpectrumList,
20
+ ReactionScheme, ReactionList as convention-bearing roots.
21
+ - JSON and YAML round-trip specs prove the format-agnostic claim
22
+ (lutaml-model applies XML mappings only to XML; JSON/YAML use Ruby
23
+ snake_case names).
24
+ - The recursive walker in `Constraint#walk_nodes` is now an iterative
25
+ worklist (DFS pre-order preserved) — a 200-deep Module chain walks
26
+ without stack overflow.
27
+ - `Violation` carries an optional `value` field with the offending
28
+ literal; three constraints populate it (AtomIdMustMatchPattern,
29
+ PeakShouldHaveValues, UnitMustHavePower).
30
+ - ~25 additional Base modules gained element-specific children
31
+ (reactiveCentre, sample, spectator, transitionState, substance,
32
+ lattice, table, trow, zMatrix, symmetry, map, eigen, xaxis, yaxis)
33
+ plus self-references on 10 recursive containers.
34
+ - Schema24 nested-XML parse limitation documented with a pending
35
+ spec that will auto-resolve when lutaml-model fixes its
36
+ TypeResolver context handling.
37
+ - 342 specs total, 0 failures, 2 pending. Zero forbidden patterns.
@@ -0,0 +1,40 @@
1
+ # Round 19 — JSON wire names, Schema24 workaround attempts, matrix coverage
2
+
3
+ This round directly addresses the user's question about `key_value`
4
+ blocks for JSON/YAML, and continues closing coverage gaps.
5
+
6
+ ## Files
7
+
8
+ - [75 — key_value mappings for JSON/YAML wire names](75-key-value-mappings.md)
9
+ - [76 — Schema24 parse workaround attempts](76-schema24-workaround.md)
10
+ - [77 — Convention coverage matrix spec](77-convention-coverage-matrix.md)
11
+ - [78 — Fixture coverage audit](78-fixture-audit.md)
12
+ - [79 — Final verification round 19](79-final-verification-round19.md)
13
+
14
+ ## Outcomes
15
+
16
+ - **Answer to user's question**: previously NO, JSON output used
17
+ Ruby snake_case names because no `key_value do ... end` blocks
18
+ were declared. Added `Base::WireMappings` mixin with
19
+ `auto_key_value_mapping!` class method — generates a parallel
20
+ key_value mapping from the existing XML mappings. JSON now
21
+ carries CML wire names (`elementType`, `atomArray`, `formalCharge`).
22
+ Single source of truth — one wire name per attribute.
23
+ - **Schema24 nested-XML parse**: ROOT CAUSE FOUND AND FIXED in
24
+ subsequent round (see TODO 76). The bug was a type-name collision:
25
+ Schema24's legacy `<string>`/`<integer>`/`<float>` CML elements
26
+ shadowed lutaml-model's primitive types in the schema24 context.
27
+ Fixed by excluding these from type registration while keeping
28
+ them as wire classes.
29
+ - **Convention coverage matrix spec**: 49 specs asserting all 8
30
+ conventions have QName, namespace, >=1 constraint, a spec file,
31
+ a Detection root role, and run detect_and_validate without
32
+ raising. Adding a new convention without full coverage makes
33
+ the matrix spec fail.
34
+ - **Fixed bug**: `CompchemModuleMustContainJobList` assumed
35
+ `node.modules` was non-nil; now defensively handles nil.
36
+ - **New fixtures**: ethanol IR (spectroscopy), methane combustion
37
+ (cascade), basic SI units (simpleUnit), ethanol parallel-array
38
+ form (parallel_array), standard unit types (unitType-dictionary).
39
+ - **422 examples, 0 failures, 0 pending**. Zero forbidden patterns.
40
+ (After TODO 76 fix: 423 examples, 0 failures.)
@@ -0,0 +1,49 @@
1
+ # Round 20 — Schema24 parse bug FIXED
2
+
3
+ This round found and fixed the long-standing Schema24 nested-XML
4
+ parse bug that had been documented as a "limitation" since round 18.
5
+
6
+ ## Root cause
7
+
8
+ `Cml::Elements::SCHEMA24_ONLY` included CML's legacy `<string>`,
9
+ `<integer>`, `<float>` elements, and Schema24::Configuration
10
+ registered them as types in the schema24 context. Their XML names
11
+ collided with lutaml-model's primitive type names. Every
12
+ `attribute :foo, :string` cast in any Schema24 wire class resolved
13
+ `:string` to `Chemicalml::Cml::Schema24::String` (a Serializable)
14
+ instead of `Lutaml::Model::Type::String` (the primitive).
15
+
16
+ The Serializable.cast path on a String value tried to deserialize
17
+ it as a CML `<string>` element, which failed. Schema3 was
18
+ unaffected because Schema 3 doesn't declare these elements.
19
+
20
+ ## Fix
21
+
22
+ Added `Cml::Elements::SCHEMA24_TYPE_COLLISIONS = %i[Float Integer
23
+ String].freeze`. Schema24::Configuration now passes this list as
24
+ `except:` when registering SCHEMA24_ONLY. The three wire classes
25
+ remain defined (parseable as document roots) but are no longer
26
+ registered as types.
27
+
28
+ ## Files
29
+
30
+ - [76 — Schema24 parse bug FIXED](76-schema24-workaround.md)
31
+
32
+ ## Outcomes
33
+
34
+ - `Chemicalml::Cml::Schema24::Document.from_xml` parses nested
35
+ content. Schema24 finally works the same as Schema3.
36
+ - `spec/chemicalml/cml/schema24_nested_parse_spec.rb` has 3
37
+ passing specs including one with parallel-array atomArray and
38
+ nested molecule.
39
+ - Removed the pending spec.
40
+ - Updated CLAUDE.md (limitation replaced with collision note).
41
+ - Updated TODO 76 with the real root cause and fix.
42
+ - Full suite: **423 examples, 0 failures, 0 pending**.
43
+
44
+ ## Lesson
45
+
46
+ When a bug seems to be in upstream but only one of two parallel
47
+ implementations is affected, look for **what's different** about
48
+ that implementation's setup. The bug wasn't in lutaml-model's
49
+ caching — it was in our registration of type-colliding names.
@@ -0,0 +1,35 @@
1
+ # Round 21 — XSD enums, reference resolution, TOML probe
2
+
3
+ This round adds the canonical enum source of truth, three
4
+ enum-validation constraints, a reference resolver for id-based
5
+ links, and a TOML round-trip probe.
6
+
7
+ ## Files
8
+
9
+ - [80 — XSD enum constants](80-xsd-enum-constants.md)
10
+ - [81 — Enum validation constraints](81-enum-validation-constraints.md)
11
+ - [82 — Reference resolver](82-reference-resolver.md)
12
+ - [83 — TOML round-trip spec](83-toml-roundtrip.md)
13
+ - [84 — Final verification round 21](84-final-verification-round21.md)
14
+
15
+ ## Outcomes
16
+
17
+ - `Chemicalml::Cml::Enums` — 31 frozen-Set constants matching every
18
+ XSD simpleType that restricts to enumerations (orderType,
19
+ stereoType, chiralityType, latticeType, matrixType, stateType,
20
+ peakMultiplicityType, etc.). A spec parses the XSD and asserts
21
+ every constant matches — drift is caught automatically.
22
+ - Three enum-validation constraints in the molecular convention
23
+ (bond order, bond stereo, molecule chirality). Warning severity
24
+ so extension values are flagged but not rejected. Each populates
25
+ `Violation.value` with the offending literal.
26
+ - `Chemicalml::Cml::ReferenceResolver` walks a document, builds an
27
+ id-index, and resolves `atomRefs2`/`atomRefs4`/`bondRefs`/`ref`
28
+ to actual wire instances. `unresolved_refs` lists every missing
29
+ target — a programmatic alternative to the constraint walker.
30
+ - TOML round-trip spec added (3 pending specs because the tomlib
31
+ adapter isn't installed; will auto-resolve when tomlib is added
32
+ to the Gemfile).
33
+ - Molecular convention now registers 26 constraints (up from 23).
34
+ - **452 examples, 0 failures, 3 pending** (TOML adapter pending).
35
+ Zero forbidden patterns.