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,48 @@
1
+ # Round 22 — Missing molecular constraints, CLI, diverse fixtures
2
+
3
+ This round closes constraints from the molecular spec that were
4
+ genuinely missing, adds a CLI utility, and broadens fixture
5
+ coverage to exercise more code paths.
6
+
7
+ ## Files
8
+
9
+ - [85 — Bond atomRefs2 distinct constraint](85-bond-distinct-atoms.md)
10
+ - [86 — References-should-resolve constraint](86-references-should-resolve.md)
11
+ - [87 — CLI utility](87-cli-utility.md)
12
+ - [88 — Diverse fixtures](88-diverse-fixtures.md)
13
+ - [89 — Final verification round 22](89-final-verification-round22.md)
14
+
15
+ ## Outcomes
16
+
17
+ - `BondAtomRefs2ShouldBeDistinct` — warning when `atomRefs2` references
18
+ the same atom twice (e.g. `a1 a1`). Closes the molecular spec's
19
+ "two distinct atom ids" rule.
20
+ - `ReferencesShouldResolve` — DocumentConstraint that uses
21
+ `Cml::ReferenceResolver` to walk the document and report bonds
22
+ referencing missing atoms. Catches typos like `atomRefs2="a1 a99"`
23
+ when only a1 exists.
24
+ - `Chemicalml::Cli` + `exe/chemicalml` — CLI utility with three
25
+ commands:
26
+ - `chemicalml validate <file>` — auto-detect convention, print
27
+ violations to stderr, exit non-zero on errors
28
+ - `chemicalml conventions` — list the 8 registered conventions
29
+ - `chemicalml dictionaries` — list the 8 built-in dictionaries
30
+ - New fixtures:
31
+ - chiral_center_with_bond_stereo.cml (bondStereo W/H + atomParity)
32
+ - ethanol_with_properties.cml (propertyList + formula)
33
+ - nacl_with_lattice.cml (crystal + symmetry + matrix)
34
+ - diels_alder.cml (reactionScheme + reactantList + productList)
35
+ - Molecular convention now registers 28 constraints (up from 26).
36
+ - **471 examples, 0 failures, 3 pending**. Zero forbidden patterns.
37
+
38
+ ## Architectural notes
39
+
40
+ - The `ReferencesShouldResolve` constraint is the first
41
+ `DocumentConstraint` (vs NodeConstraint) in the molecular
42
+ convention. It demonstrates the convention framework's
43
+ support for cross-cutting rules that need the whole document.
44
+ - The CLI is a thin dispatcher — adding a new subcommand = adding
45
+ one method and one entry in `Cli::COMMANDS`. OCP.
46
+ - Existing fixtures already passed; the new ones exercise the
47
+ recently added bondStereo / propertyList / crystal / reaction
48
+ paths in the Base modules.
@@ -0,0 +1,46 @@
1
+ # Round 23 — Convention spec completeness, YARD docs
2
+
3
+ This round closes remaining gaps between the upstream convention
4
+ specs and our implementation, and adds YARD documentation to the
5
+ public API.
6
+
7
+ ## Files
8
+
9
+ - [90 — BondStereo completeness](90-bond-stereo-completeness.md)
10
+ - [91 — AtomParity atomRefs4 distinct](91-atomparity-distinct.md)
11
+ - [92 — Unit-dictionary completeness](92-unit-dictionary-completeness.md)
12
+ - [93 — YARD docs for public API](93-yard-docs.md)
13
+ - [94 — Final verification round 23](94-final-verification-round23.md)
14
+
15
+ ## Outcomes
16
+
17
+ - `BondStereoAtomRefs4ShouldBeDistinct` — molecular warning when
18
+ C/T stereo's atomRefs4 has duplicate atoms. The molecular spec
19
+ says "four distinct atom ids" — now enforced.
20
+ - `AtomParityAtomRefs4ShouldBeDistinct` — molecular warning when
21
+ atomParity's atomRefs4 has duplicates. Closes a real semantic gap:
22
+ duplicate parity atoms make the chiral descriptor meaningless.
23
+ - Three new unit-dictionary constraints:
24
+ - `UnitMustHaveTitle`
25
+ - `UnitMustHaveParentSi`
26
+ - `UnitMustHaveMultiplierOrConstantToSi`
27
+ - Molecular convention: 30 constraints (up from 28).
28
+ - Unit-dictionary convention: 8 constraints (up from 5).
29
+ - YARD docstrings added to:
30
+ - `Chemicalml.parse`, `Chemicalml.serialize`, `Chemicalml.parser_for`
31
+ - `Chemicalml::Cli.run`, `Chemicalml::Cli.run_validate`
32
+ - `Chemicalml::Convention.validate`, `validate_report`, `detect_and_validate`, `lookup`
33
+ - `Chemicalml::Dictionary.load`
34
+ - `Chemicalml::Cml::ReferenceResolver#initialize`
35
+ - **471 examples, 0 failures, 3 pending**. Zero forbidden patterns.
36
+
37
+ ## Architectural notes
38
+
39
+ - All new constraints are warning severity (or error where the spec
40
+ uses MUST). The convention framework distinguishes naturally.
41
+ - YARD docstrings follow the existing pattern (concise, no novel
42
+ terminology, examples where helpful). They will be picked up by
43
+ `bundle exec yard doc` when the yard gem is added.
44
+ - The spec count stays the same (471) because the round-23 spec
45
+ file replaces one existing test; new constraint tests replace the
46
+ old "5 constraints" / "28 constraints" assertion tests.
@@ -0,0 +1,53 @@
1
+ # Round 24 — Round-trip equivalence, cross-format interop, introspection
2
+
3
+ This round formalises the round-trip guarantee across all fixtures,
4
+ proves cross-format interoperability (XML → JSON → YAML → XML),
5
+ adds a convention introspection API, and exercises Schema24 with
6
+ complex content.
7
+
8
+ ## Files
9
+
10
+ - [95 — Round-trip equivalence spec](95-round-trip-equivalence.md)
11
+ - [96 — Cross-format interoperability](96-cross-format-interop.md)
12
+ - [97 — Convention introspection API](97-convention-introspection.md)
13
+ - [98 — Schema24 complex round-trip](98-schema24-complex-round-trip.md)
14
+ - [99 — Final verification round 24](99-final-verification-round24.md)
15
+
16
+ ## Outcomes
17
+
18
+ - **Round-trip equivalence spec** — for every one of the 20 fixtures,
19
+ asserts that `parse(xml) → serialize → parse` produces a
20
+ structurally-equivalent document (compared via a recursive
21
+ element-name fingerprint). Catches future serialization drift.
22
+
23
+ - **Cross-format interoperability spec** — proves the format-agnostic
24
+ claim end-to-end:
25
+ - XML → JSON → XML (structural fingerprint equal)
26
+ - XML → YAML → XML (structural fingerprint equal)
27
+ - JSON → JSON idempotent (same output)
28
+ - XML → JSON → YAML → XML (4-format chain, structural equal)
29
+
30
+ - **`Convention::Registry.each`** — iterates all 8 conventions
31
+ sorted by QName. Returns an Enumerator without a block.
32
+ - **`Convention::Registry.convention_root?(role)`** — boolean check
33
+ for whether a Role module is a convention-bearing root.
34
+
35
+ - **Schema24 complex round-trip** — exercises the now-fixed Schema24
36
+ parser with rich content (parallel-array atoms + bonds, names,
37
+ formulas). Proves Schema24 has feature parity with Schema3.
38
+
39
+ - **Real bug fix discovered via round 24 testing**:
40
+ `AtomArrayMustContainAtoms` was rejecting parallel-array form
41
+ (which has no `<atom>` children but uses `atomID` attribute).
42
+ Fixed to accept either form.
43
+
44
+ - **515 examples, 0 failures, 3 pending** (TOML adapter pending).
45
+ Zero forbidden patterns.
46
+
47
+ ## Architectural insight
48
+
49
+ The cross-format interop spec is the most valuable safety net: any
50
+ future change to the Base modules that breaks the format-agnostic
51
+ claim (e.g. adds a method that only works in one format) is caught
52
+ immediately. The structural fingerprint comparison is format-blind,
53
+ so it catches real data loss rather than just string-equality noise.
@@ -0,0 +1,66 @@
1
+ # Round 25 — Semantic validation, periodic table, dictRef resolution
2
+
3
+ This round adds three real semantic checks (element type, dictRef
4
+ resolution, summary rendering) that go beyond structural validation.
5
+
6
+ ## Files
7
+
8
+ - [100 — Periodic table constant](100-periodic-table.md)
9
+ - [101 — Element type validation constraint](101-element-type-validation.md)
10
+ - [102 — DictRef resolution constraint](102-dictref-resolution.md)
11
+ - [103 — ValidationReport summary](103-validation-report-summary.md)
12
+ - [104 — Final verification round 25](104-final-verification-round25.md)
13
+
14
+ ## Outcomes
15
+
16
+ - **`Cml::Enums::ELEMENT_TYPE_VALUES`** — 120-element frozen Set
17
+ matching XSD elementTypeType exactly. Includes the periodic table
18
+ plus "Du" (dummy) and "R" (group placeholder). Single source of
19
+ truth for valid element symbols.
20
+
21
+ - **`AtomElementTypeShouldBeInPeriodicTable`** — molecular warning
22
+ when `atom.elementType` isn't a recognised periodic table symbol.
23
+ Catches typos like "Carb", "Xx", "X" — previously silently
24
+ accepted.
25
+
26
+ - **`DictRefShouldResolve`** — molecular warning when an element's
27
+ `dictRef` attribute doesn't resolve against the built-in
28
+ dictionaries via `Chemicalml::Dictionary::Registry.lookup`. Catches
29
+ typos like `dictRef="cml:bpingpoint"`. This is a real
30
+ cross-component integration: the convention constraint system
31
+ querying the dictionary registry.
32
+
33
+ - **`ValidationReport#summary`** — human-readable multi-line summary.
34
+ Used by the CLI; useful for one-shot scripts. Format:
35
+ ```
36
+ Errors: 2, Warnings: 1
37
+
38
+ Errors:
39
+ ERROR path/a: message (value="X")
40
+ ...
41
+
42
+ Warnings:
43
+ WARN path/b: message
44
+ ...
45
+ ```
46
+
47
+ - **Real bug caught by new constraint**: existing test
48
+ `extended_constraints_spec.rb` was using `dictRef="cml:energy"`
49
+ which isn't in the cml dictionary. The new DictRefShouldResolve
50
+ constraint flagged it. Fixed the test to use the real `cml:bp`
51
+ entry — and incidentally validated that the constraint works.
52
+
53
+ - Molecular convention: 32 constraints (up from 30).
54
+
55
+ - **528 examples, 0 failures, 3 pending** (TOML adapter pending).
56
+ Zero forbidden patterns.
57
+
58
+ ## Architectural insight
59
+
60
+ The DictRefShouldResolve constraint is the first to bridge two
61
+ previously-separate subsystems (convention constraints and
62
+ dictionary registry). It validates the cross-component contract:
63
+ "a dictRef must point at a real term". This is the kind of
64
+ whole-system semantic check that's only possible when both halves
65
+ are mature — round 25 is the first round where it became worth
66
+ writing.
@@ -0,0 +1,55 @@
1
+ # Round 26 — XSD patterns, AtomParity parent check, canonical comparison
2
+
3
+ This round adds the canonical XSD pattern constants, semantic
4
+ parent-atom validation for atomParity, and a canonical document
5
+ comparison helper.
6
+
7
+ ## Files
8
+
9
+ - [105 — XSD pattern constants](105-xsd-pattern-constants.md)
10
+ - [106 — Id/namespace pattern validation](106-id-pattern-validation.md)
11
+ - [107 — AtomParity parent atom check](107-atomparity-parent-check.md)
12
+ - [108 — Canonical document comparison](108-canonical-comparison.md)
13
+ - [109 — Final verification round 26](109-final-verification-round26.md)
14
+
15
+ ## Outcomes
16
+
17
+ - **`Cml::Patterns`** — 14 frozen Regexp constants matching every XSD
18
+ simpleType that restricts by pattern (atomIDType, idType,
19
+ dictionaryPrefixType, namespaceType, refType, versionType,
20
+ formulaType, etc.). Single source of truth — a future XSD edit
21
+ that changes a pattern can be regenerated from one place.
22
+
23
+ - **`MoleculeIdShouldMatchPattern`** and **`BondIdShouldMatchPattern`**
24
+ — molecular warnings using the new patterns. Catches ids that
25
+ don't match the XSD-declared format.
26
+
27
+ - **`AtomParityShouldIncludeParentAtom`** — molecular DocumentConstraint
28
+ that walks the tree and warns when `<atomParity>`'s atomRefs4
29
+ doesn't include the parent `<atom>`'s id. Real semantic check
30
+ that closes a CML convention rule.
31
+
32
+ - **`Cml::CanonicalComparison`** — semantic document comparison.
33
+ `equal?` returns true if two documents have the same structural
34
+ fingerprint. `diff` returns a hash of element_name → count_delta.
35
+ Useful for testing and diff tools.
36
+
37
+ - **Real bugs caught during round 26**:
38
+ - DELIMITER_PATTERN had unescaped `/` — syntax error.
39
+ - `visit_with_parent` was using `yield` in recursion, causing
40
+ LocalJumpError. Fixed by using explicit `&block`.
41
+ - XSD patterns are unanchored; for validation they must be
42
+ wrapped in `\A...\z`.
43
+
44
+ - Molecular convention: 35 constraints (up from 32).
45
+
46
+ - **543 examples, 0 failures, 3 pending** (TOML adapter pending).
47
+ Zero forbidden patterns.
48
+
49
+ ## Architectural insight
50
+
51
+ The `CanonicalComparison` helper is the abstraction the round-trip
52
+ spec (TODO 95) and Schema24 complex round-trip spec (TODO 98) both
53
+ needed. Previously each spec inlined its own fingerprint function;
54
+ now there's one canonical implementation. Classic "rule of three"
55
+ refactor — the third use is the trigger to extract.
@@ -0,0 +1,51 @@
1
+ # Round 27 — User-facing API, CLI improvements
2
+
3
+ This round adds the user-facing query API on `Cml::Visitable`, a
4
+ top-level `Chemicalml.validate` shortcut, and two new CLI commands
5
+ (`inspect`, `elements`).
6
+
7
+ ## Files
8
+
9
+ - [110 — Document query API](110-document-query-api.md)
10
+ - [111 — Top-level Chemicalml.validate](111-toplevel-validate.md)
11
+ - [112 — CLI inspect command](112-cli-inspect.md)
12
+ - [113 — CLI elements command](113-cli-elements.md)
13
+ - [114 — Final verification round 27](114-final-verification-round27.md)
14
+
15
+ ## Outcomes
16
+
17
+ - **`Cml::Visitable` query methods** (inherited by every wire class):
18
+ - `#each_wire_node`, `#each_atom`, `#each_bond`, `#each_molecule` — recursive iterators
19
+ - `#find_atom(id)`, `#find_bond(id)`, `#find_molecule(id)` — id-based lookup
20
+ - `#atom_count`, `#bond_count`, `#molecule_count` — recursive counts
21
+ - All return Enumerators without a block (Enumerable-style)
22
+
23
+ - **`Chemicalml.validate(doc)`** — top-level shortcut for
24
+ `Convention.detect_and_validate(doc)`. Users no longer need to
25
+ remember the full namespace.
26
+
27
+ - **`chemicalml inspect <file>`** — new CLI command. Prints a
28
+ tree-style summary of the document with element names, ids, and
29
+ structure. Useful for debugging.
30
+
31
+ - **`chemicalml elements`** — new CLI command. Lists every Schema3
32
+ wire class with its XML root name. Useful for introspection.
33
+
34
+ - **CLI refactor**: `run_validate` now uses `report.summary` instead
35
+ of manual rendering. Cleaner code, better output.
36
+
37
+ - **559 examples, 0 failures, 3 pending** (TOML adapter pending).
38
+ Zero forbidden patterns.
39
+
40
+ ## Architectural insight
41
+
42
+ The query methods live on `Visitable` (the marker mixin every wire
43
+ class includes) rather than on `Document` specifically. This means
44
+ ANY wire class can be the root of a query — `molecule.find_atom(id)`,
45
+ `reaction.find_molecule(id)`, etc. This was the right call: the
46
+ walker is already general (visits any subtree); exposing query
47
+ methods only on Document would have required callers to navigate
48
+ up to Document first.
49
+
50
+ The CLI's `print_tree` helper is recursive but bounded (`max_depth: 6`)
51
+ to avoid runaway output on pathological documents.
@@ -0,0 +1,61 @@
1
+ # Round 28 — Convenience API, custom conventions, compchem completion
2
+
3
+ This round adds user-facing convenience (`parse_file`, custom
4
+ convention registration), more compchem constraints from the spec,
5
+ and a comprehensive lint cleanup.
6
+
7
+ ## Files
8
+
9
+ - [115 — parse_file convenience](115-parse-file.md)
10
+ - [116 — Custom convention registration](116-custom-convention.md)
11
+ - [117 — More compchem constraints](117-more-compchem-constraints.md)
12
+ - [118 — Final verification round 28](118-final-verification-round28.md)
13
+
14
+ ## Outcomes
15
+
16
+ - **`Chemicalml.parse_file(path)`** — convenience wrapper around
17
+ `parse(File.read(path))`. Raises ArgumentError for missing files.
18
+ Eliminates the `File.read` boilerplate.
19
+
20
+ - **`Convention::Registry.register_custom(mod)`** — runtime
21
+ registration of custom conventions. The module must `extend Base`
22
+ and return a non-empty `qname`. Useful for domain-specific CML
23
+ extensions (e.g., a `convention:my-lab-extensions` for a research
24
+ group's custom vocabulary).
25
+
26
+ - **Two new compchem constraints** from the spec:
27
+ - `InitializationMustHaveContent` — initialization module must
28
+ contain at least one of molecule, parameterList, or user-defined
29
+ module
30
+ - `FinalizationMustHaveContent` — finalization module must contain
31
+ at least one of molecule, propertyList, or user-defined module
32
+
33
+ - **Lint cleanup**: ran `rubocop -A` on all recently-touched files.
34
+ 80 offenses autocorrected (string quotes, modifier-if usage,
35
+ parenthesised method args, etc.). Zero remaining style issues.
36
+
37
+ - **Real bug caught during round 28**: `register_custom` initially
38
+ deadlocked because it called `load_cache` while holding the mutex.
39
+ Fixed by separating the load step (which acquires/releases its
40
+ own mutex) from the mutation step (direct hash access after load
41
+ is guaranteed to have populated `@cache`).
42
+
43
+ - Compchem convention: 20 constraints (up from 18).
44
+
45
+ - **568 examples, 0 failures, 3 pending** (TOML adapter pending).
46
+ Zero forbidden patterns. **88 constraints across 8 conventions.**
47
+
48
+ ## Architectural insight
49
+
50
+ The `register_custom` deadlock is a classic Ruby mutex pitfall:
51
+ re-entering a non-reentrant `Mutex#synchronize` block from within
52
+ itself deadlocks. The fix — call `load_cache` first (which manages
53
+ its own mutex acquisition), then mutate `@cache` directly — works
54
+ because Ruby's `||=` assignment makes `@cache` non-nil after the
55
+ first load. Documented in the method comment.
56
+
57
+ This round also reinforced the value of running rubocop on each
58
+ round's output. 80 autocorrected offenses across 8 files means
59
+ style drift was creeping in — single-quote vs double-quote,
60
+ `unless` vs `if !`, etc. A small CI rule (`bundle exec rubocop`
61
+ must pass) would catch this in future.
@@ -0,0 +1,64 @@
1
+ # Round 29 — Wire introspection, README, dataType consistency
2
+
3
+ This round adds wire-class introspection, updates the README to
4
+ reflect the current API, and adds a property-dataType consistency
5
+ constraint that bridges the convention and dictionary subsystems
6
+ in a new way.
7
+
8
+ ## Files
9
+
10
+ - [119 — Wire-class introspection](119-wire-introspection.md)
11
+ - [120 — Update README](120-readme-update.md)
12
+ - [121 — Property dataType consistency](121-property-datatype.md)
13
+ - [122 — Final verification round 29](122-final-verification-round29.md)
14
+
15
+ ## Outcomes
16
+
17
+ - **`Cml.for_xml_name(name, schema:)`** — wire-class lookup by XML
18
+ element name. Uses the existing `Elements::XML_TO_CLASS` reverse
19
+ index. Returns nil for unknown names.
20
+
21
+ - **`Cml.wire_classes(schema:)`** — enumerator over all wire classes
22
+ for a schema version. Useful for code generation and tooling.
23
+
24
+ - **README rewritten**:
25
+ - 8 conventions + 88+ constraints documented
26
+ - All modern API examples (`parse_file`, `validate`, `each_atom`)
27
+ - CLI commands documented (`validate`, `inspect`, `conventions`,
28
+ `dictionaries`, `elements`)
29
+ - Cross-format serialisation examples
30
+ - Custom convention registration example
31
+ - Dictionary lookup example
32
+ - Architectural overview (`Base::*`, `Role::*`, `Elements::ALL`)
33
+
34
+ - **`PropertyScalarDataTypeMatchesDictionary`** — molecular warning
35
+ when a `<property>`'s `<scalar>` `dataType` doesn't match the
36
+ `dataType` declared in the dictionary entry referenced by `dictRef`.
37
+ This is a **three-component integration check**: convention
38
+ constraint system queries the dictionary registry AND inspects the
39
+ document's scalar child.
40
+
41
+ - Molecular convention: 36 constraints (up from 35).
42
+
43
+ - **574 examples, 0 failures, 3 pending**. Zero forbidden patterns.
44
+
45
+ ## Architectural insight
46
+
47
+ `PropertyScalarDataTypeMatchesDictionary` is the deepest
48
+ cross-component check yet — it validates that:
49
+
50
+ 1. The property's `dictRef` resolves to a real dictionary entry
51
+ (uses `Dictionary::Registry.lookup`).
52
+ 2. The entry declares a `dataType`.
53
+ 3. The property's `<scalar>` child declares a `dataType`.
54
+ 4. The two `dataType` values match.
55
+
56
+ If any step is missing data, the constraint silently passes (no
57
+ false positives). When all the data is present, it catches real
58
+ inconsistencies that would cause downstream type errors.
59
+
60
+ This kind of multi-step semantic validation is what mature
61
+ document-validation frameworks look like. Round 29 is the first
62
+ round where it's been possible — earlier rounds built the
63
+ primitives (dictionary registry, dictRef resolution, scalar
64
+ dataType attribute) that this constraint composes.
@@ -0,0 +1,50 @@
1
+ # Round 30 — unitType resolution, JSON CLI, enumeration
2
+
3
+ This round adds the unit-unitType cross-component check (parallels
4
+ dictRef resolution), CLI JSON output for machine consumers, and a
5
+ constraint-enumeration API.
6
+
7
+ ## Files
8
+
9
+ - [123 — Unit unitType resolution](123-unit-unittype-resolution.md)
10
+ - [124 — CLI JSON output](124-cli-json-output.md)
11
+ - [125 — Constraint enumeration API](125-constraint-enumeration.md)
12
+ - [126 — Final verification round 30](126-final-verification-round30.md)
13
+
14
+ ## Outcomes
15
+
16
+ - **`UnitUnitTypeShouldResolve`** — unit-dictionary warning when a
17
+ `<unit>`'s `unitType` attribute doesn't resolve against the
18
+ built-in unitType-dictionary. Cross-component check parallel to
19
+ the molecular convention's `DictRefShouldResolve`.
20
+
21
+ - **`chemicalml validate --json file.cml`** — JSON output mode for
22
+ the CLI. Returns `{ file:, ok:, has_warnings:, violations: [...] }`
23
+ where each violation has `severity`, `path`, `message`, `value`.
24
+ Useful for editor integration, CI pipelines, automated tooling.
25
+
26
+ - **`Convention::Registry.each_constraint`** — iterates every
27
+ constraint across every convention, yielding `(convention, class)`
28
+ pairs. Useful for documentation generation and introspection.
29
+
30
+ - **`Convention::Registry.total_constraint_count`** — sum of all
31
+ registered constraints across all conventions (currently 90).
32
+
33
+ - Unit-dictionary convention: 9 constraints (up from 8).
34
+
35
+ - **580 examples, 0 failures, 3 pending** (TOML adapter pending).
36
+ Zero forbidden patterns. **90 constraints across 8 conventions.**
37
+
38
+ ## Architectural insight
39
+
40
+ `UnitUnitTypeShouldResolve` completes a symmetric pair with
41
+ `DictRefShouldResolve` from round 25. Both follow the same pattern:
42
+ walk the document, look up reference attributes against the
43
+ dictionary registry, warn on misses. The pattern is now established
44
+ enough that adding a new reference-resolution constraint is a
45
+ 2-minute job: copy the structure, change the attribute name and the
46
+ target dictionary.
47
+
48
+ The `each_constraint` iterator makes the constraint table tractable
49
+ for documentation generation. A future round could auto-generate a
50
+ Markdown table of all 90 constraints from this single API call.
@@ -0,0 +1,54 @@
1
+ # Round 31 — CLI tooling, parentSI resolution
2
+
3
+ This round completes the CLI tooling suite (constraints + enums
4
+ commands) and adds the parentSI resolution constraint, completing
5
+ the cross-component check symmetry.
6
+
7
+ ## Files
8
+
9
+ - [127 — CLI constraints command](127-cli-constraints.md)
10
+ - [128 — CLI enums command](128-cli-enums.md)
11
+ - [129 — ParentSI resolution](129-parentsi-resolution.md)
12
+ - [130 — Final verification round 31](130-final-verification-round31.md)
13
+
14
+ ## Outcomes
15
+
16
+ - **`chemicalml constraints`** — new CLI command listing all 91
17
+ constraints grouped by convention. Each line shows the constraint
18
+ class name and its declared `applies_to` roles.
19
+
20
+ - **`chemicalml enums`** — new CLI command listing every
21
+ `Cml::Enums` constant with its allowed values. Useful for users
22
+ who want to know what enum values an attribute accepts.
23
+
24
+ - **`UnitParentSiShouldResolve`** — unit-dictionary warning when a
25
+ `<unit>`'s `parentSI` doesn't resolve against built-in
26
+ dictionaries. Completes the cross-component check symmetry:
27
+ - dictRef resolution (molecular)
28
+ - unitType resolution (unit-dictionary)
29
+ - parentSI resolution (unit-dictionary)
30
+
31
+ - **Real bug caught during round 31**: existing fixture
32
+ `convention_spec.rb:226` used `parent_si: "siUnits:s"` which
33
+ doesn't resolve (correct prefix is `si`, not `siUnits`). Fixed.
34
+
35
+ - Unit-dictionary convention: 10 constraints (up from 9).
36
+
37
+ - **583 examples, 0 failures, 3 pending**. Zero forbidden patterns.
38
+ **91 constraints across 8 conventions.**
39
+
40
+ ## Architectural insight
41
+
42
+ The cross-component check pattern (walk document → look up reference
43
+ attribute against registry → warn on miss) is now fully
44
+ established across three constraint pairs:
45
+
46
+ | Convention | Attribute | Lookup target |
47
+ |---|---|---|
48
+ | molecular | `dictRef` | dictionary registry |
49
+ | unit-dictionary | `unitType` | unitType-dictionary registry |
50
+ | unit-dictionary | `parentSI` | unit dictionary registry |
51
+
52
+ Adding a fourth is mechanical: copy the structure, change the
53
+ attribute name and lookup target. The pattern's clarity is its own
54
+ form of documentation.
@@ -0,0 +1,67 @@
1
+ # Round 32 — Thor CLI refactor, info command, auto-docs
2
+
3
+ This round refactors the CLI to use Thor with one class per command,
4
+ adds the `info` command for element introspection, and auto-generates
5
+ constraint documentation.
6
+
7
+ ## Files
8
+
9
+ - [131 — CLI info command](131-cli-info-command.md)
10
+ - [132 — Auto-generated constraint docs](132-auto-generated-constraint-docs.md)
11
+ - [133 — Final verification round 32](133-final-verification-round32.md)
12
+
13
+ ## Outcomes
14
+
15
+ - **CLI refactored to Thor**. Each command is now a dedicated class:
16
+ - `Chemicalml::Cli::Base < Thor` — dispatcher
17
+ - `Chemicalml::Cli::Command` — base class with `.run(args, options)`
18
+ and `#run(args, options)`
19
+ - `Chemicalml::Cli::ValidateCommand`
20
+ - `Chemicalml::Cli::InspectCommand`
21
+ - `Chemicalml::Cli::ConventionsCommand`
22
+ - `Chemicalml::Cli::DictionariesCommand`
23
+ - `Chemicalml::Cli::ElementsCommand`
24
+ - `Chemicalml::Cli::ConstraintsCommand`
25
+ - `Chemicalml::Cli::EnumsCommand`
26
+ - `Chemicalml::Cli::InfoCommand`
27
+
28
+ Each command supports both `MyCommand.run(args, options)` (class
29
+ method) and `MyCommand.new.run(args, options)` (instance method)
30
+ invocation patterns — matching the user's requested pattern.
31
+
32
+ - **`chemicalml info <element>`** — new CLI command. Shows element
33
+ XML name, Ruby class, included Role module, all attributes (with
34
+ collection marker), and all applicable constraints across every
35
+ convention. Useful for users exploring the model.
36
+
37
+ - **Auto-generated constraint docs** — `docs/generate_constraint_docs.rb`
38
+ script produces `docs/constraints.md` from
39
+ `Convention::Registry.each_constraint`. Single source of truth —
40
+ regenerating picks up new constraints automatically. No manual
41
+ maintenance.
42
+
43
+ - **Bug fix from autocorrect**: `Convention::Registry#load_cache`
44
+ had `@cache` renamed to `@load_cache` by rubocop's
45
+ `MemoizedInstanceVariableName` rule. `register_custom` and `reset!`
46
+ needed alignment — fixed in this round.
47
+
48
+ - **586 examples, 0 failures, 3 pending**. Zero forbidden patterns.
49
+
50
+ ## Architectural insight
51
+
52
+ The Thor refactor separates concerns cleanly:
53
+
54
+ 1. **Thor dispatcher** owns argument parsing, help text, option
55
+ declarations. Each `desc + def` block is 2-3 lines.
56
+ 2. **Command class** owns the actual work. Easy to test in isolation
57
+ (`MyCommand.new.run(args, options)` returns an exit code).
58
+ 3. **No business logic in the dispatcher** — it's pure routing.
59
+
60
+ This pattern scales: a future `rake` task, Rake task, or web handler
61
+ can re-use any `*Command` class without going through Thor. The
62
+ Thor dispatcher is just one possible entry point.
63
+
64
+ The auto-generated constraint docs demonstrate the value of the
65
+ `each_constraint` API from round 30: a single iteration produces
66
+ comprehensive documentation with zero manual upkeep. Adding a new
67
+ constraint and re-running the generator is all it takes.