chemicalml 0.1.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 (248) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +20 -0
  4. data/CHANGELOG.md +21 -0
  5. data/CLAUDE.md +198 -0
  6. data/Gemfile +14 -0
  7. data/LICENSE +24 -0
  8. data/NOTICES.adoc +115 -0
  9. data/README.adoc +87 -0
  10. data/Rakefile +8 -0
  11. data/TODO.align/01-cml-base-mixins.md +122 -0
  12. data/TODO.align/02-real-schema-hierarchies.md +88 -0
  13. data/TODO.align/03-configuration.md +50 -0
  14. data/TODO.align/04-parse-entry-points.md +59 -0
  15. data/TODO.align/05-schema-aware-translator.md +33 -0
  16. data/TODO.align/06-no-respond-to.md +44 -0
  17. data/TODO.align/07-canon-xml-comparison.md +43 -0
  18. data/TODO.align/08-expand-fixtures.md +37 -0
  19. data/TODO.align/09-final-spec-lint.md +29 -0
  20. data/TODO.align/10-role-marker-modules.md +54 -0
  21. data/TODO.align/11-dry-configuration.md +63 -0
  22. data/TODO.align/12-wire-class-macro.md +51 -0
  23. data/TODO.align/13-wire-namespace.md +44 -0
  24. data/TODO.align/14-schema24-correctness.md +37 -0
  25. data/TODO.align/15-canon-fixture-roundtrip.md +38 -0
  26. data/TODO.align/16-schema24-convention-coverage.md +33 -0
  27. data/TODO.align/17-final-spec-lint.md +28 -0
  28. data/TODO.align/README.md +27 -0
  29. data/TODO.align/README.round2.md +24 -0
  30. data/TODO.cml-full/01-reference-docs.md +43 -0
  31. data/TODO.cml-full/02-schema-versioned-model.md +70 -0
  32. data/TODO.cml-full/03-fixtures-scrape.md +52 -0
  33. data/TODO.cml-full/04-convention-framework.md +69 -0
  34. data/TODO.cml-full/05-dictionary-layer.md +77 -0
  35. data/TODO.cml-full/06-extend-canonical-translator.md +60 -0
  36. data/TODO.cml-full/07-attribution-notices.md +42 -0
  37. data/TODO.cml-full/08-autoload-only.md +48 -0
  38. data/TODO.cml-full/09-spec-coverage.md +38 -0
  39. data/TODO.cml-full/README.md +23 -0
  40. data/chemicalml.gemspec +35 -0
  41. data/data/dictionaries/_index.yaml +14 -0
  42. data/data/dictionaries/compchem.yaml +481 -0
  43. data/data/dictionaries/unit_non_si.yaml +53 -0
  44. data/data/dictionaries/unit_si.yaml +87 -0
  45. data/data/dictionaries/unit_type.yaml +113 -0
  46. data/lib/chemicalml/cml/array.rb +10 -0
  47. data/lib/chemicalml/cml/atom.rb +10 -0
  48. data/lib/chemicalml/cml/atom_array.rb +10 -0
  49. data/lib/chemicalml/cml/atom_parity.rb +10 -0
  50. data/lib/chemicalml/cml/base/array.rb +36 -0
  51. data/lib/chemicalml/cml/base/atom.rb +38 -0
  52. data/lib/chemicalml/cml/base/atom_array.rb +22 -0
  53. data/lib/chemicalml/cml/base/atom_parity.rb +24 -0
  54. data/lib/chemicalml/cml/base/bond.rb +28 -0
  55. data/lib/chemicalml/cml/base/bond_array.rb +22 -0
  56. data/lib/chemicalml/cml/base/bond_stereo.rb +28 -0
  57. data/lib/chemicalml/cml/base/cml_module.rb +40 -0
  58. data/lib/chemicalml/cml/base/dictionary.rb +32 -0
  59. data/lib/chemicalml/cml/base/dictionary_entry.rb +34 -0
  60. data/lib/chemicalml/cml/base/document.rb +26 -0
  61. data/lib/chemicalml/cml/base/formula.rb +38 -0
  62. data/lib/chemicalml/cml/base/identifier.rb +26 -0
  63. data/lib/chemicalml/cml/base/label.rb +28 -0
  64. data/lib/chemicalml/cml/base/list.rb +36 -0
  65. data/lib/chemicalml/cml/base/matrix.rb +38 -0
  66. data/lib/chemicalml/cml/base/metadata.rb +30 -0
  67. data/lib/chemicalml/cml/base/metadata_list.rb +28 -0
  68. data/lib/chemicalml/cml/base/molecule.rb +40 -0
  69. data/lib/chemicalml/cml/base/name.rb +26 -0
  70. data/lib/chemicalml/cml/base/parameter.rb +34 -0
  71. data/lib/chemicalml/cml/base/parameter_list.rb +28 -0
  72. data/lib/chemicalml/cml/base/product.rb +22 -0
  73. data/lib/chemicalml/cml/base/product_list.rb +22 -0
  74. data/lib/chemicalml/cml/base/property.rb +34 -0
  75. data/lib/chemicalml/cml/base/property_list.rb +28 -0
  76. data/lib/chemicalml/cml/base/reactant.rb +22 -0
  77. data/lib/chemicalml/cml/base/reactant_list.rb +22 -0
  78. data/lib/chemicalml/cml/base/reaction.rb +32 -0
  79. data/lib/chemicalml/cml/base/reaction_list.rb +22 -0
  80. data/lib/chemicalml/cml/base/scalar.rb +32 -0
  81. data/lib/chemicalml/cml/base/substance.rb +30 -0
  82. data/lib/chemicalml/cml/base/unit.rb +38 -0
  83. data/lib/chemicalml/cml/base/unit_list.rb +30 -0
  84. data/lib/chemicalml/cml/base/unit_type.rb +28 -0
  85. data/lib/chemicalml/cml/base/unit_type_list.rb +30 -0
  86. data/lib/chemicalml/cml/base.rb +51 -0
  87. data/lib/chemicalml/cml/bond.rb +10 -0
  88. data/lib/chemicalml/cml/bond_array.rb +10 -0
  89. data/lib/chemicalml/cml/bond_stereo.rb +10 -0
  90. data/lib/chemicalml/cml/cml_module.rb +10 -0
  91. data/lib/chemicalml/cml/dictionary.rb +10 -0
  92. data/lib/chemicalml/cml/dictionary_entry.rb +10 -0
  93. data/lib/chemicalml/cml/document.rb +10 -0
  94. data/lib/chemicalml/cml/elements.rb +58 -0
  95. data/lib/chemicalml/cml/formula.rb +10 -0
  96. data/lib/chemicalml/cml/identifier.rb +10 -0
  97. data/lib/chemicalml/cml/label.rb +10 -0
  98. data/lib/chemicalml/cml/list.rb +10 -0
  99. data/lib/chemicalml/cml/matrix.rb +10 -0
  100. data/lib/chemicalml/cml/metadata.rb +10 -0
  101. data/lib/chemicalml/cml/metadata_list.rb +10 -0
  102. data/lib/chemicalml/cml/molecule.rb +10 -0
  103. data/lib/chemicalml/cml/name.rb +10 -0
  104. data/lib/chemicalml/cml/namespace.rb +17 -0
  105. data/lib/chemicalml/cml/parameter.rb +10 -0
  106. data/lib/chemicalml/cml/parameter_list.rb +10 -0
  107. data/lib/chemicalml/cml/product.rb +10 -0
  108. data/lib/chemicalml/cml/product_list.rb +10 -0
  109. data/lib/chemicalml/cml/property.rb +10 -0
  110. data/lib/chemicalml/cml/property_list.rb +10 -0
  111. data/lib/chemicalml/cml/reactant.rb +10 -0
  112. data/lib/chemicalml/cml/reactant_list.rb +10 -0
  113. data/lib/chemicalml/cml/reaction.rb +10 -0
  114. data/lib/chemicalml/cml/reaction_list.rb +10 -0
  115. data/lib/chemicalml/cml/role/array.rb +13 -0
  116. data/lib/chemicalml/cml/role/atom.rb +13 -0
  117. data/lib/chemicalml/cml/role/atom_array.rb +13 -0
  118. data/lib/chemicalml/cml/role/atom_parity.rb +13 -0
  119. data/lib/chemicalml/cml/role/bond.rb +13 -0
  120. data/lib/chemicalml/cml/role/bond_array.rb +13 -0
  121. data/lib/chemicalml/cml/role/bond_stereo.rb +13 -0
  122. data/lib/chemicalml/cml/role/cml_module.rb +14 -0
  123. data/lib/chemicalml/cml/role/dictionary.rb +13 -0
  124. data/lib/chemicalml/cml/role/dictionary_entry.rb +13 -0
  125. data/lib/chemicalml/cml/role/document.rb +13 -0
  126. data/lib/chemicalml/cml/role/formula.rb +13 -0
  127. data/lib/chemicalml/cml/role/identifier.rb +13 -0
  128. data/lib/chemicalml/cml/role/label.rb +13 -0
  129. data/lib/chemicalml/cml/role/list.rb +13 -0
  130. data/lib/chemicalml/cml/role/matrix.rb +13 -0
  131. data/lib/chemicalml/cml/role/metadata.rb +13 -0
  132. data/lib/chemicalml/cml/role/metadata_list.rb +13 -0
  133. data/lib/chemicalml/cml/role/molecule.rb +13 -0
  134. data/lib/chemicalml/cml/role/name.rb +13 -0
  135. data/lib/chemicalml/cml/role/parameter.rb +13 -0
  136. data/lib/chemicalml/cml/role/parameter_list.rb +13 -0
  137. data/lib/chemicalml/cml/role/product.rb +13 -0
  138. data/lib/chemicalml/cml/role/product_list.rb +13 -0
  139. data/lib/chemicalml/cml/role/property.rb +13 -0
  140. data/lib/chemicalml/cml/role/property_list.rb +13 -0
  141. data/lib/chemicalml/cml/role/reactant.rb +13 -0
  142. data/lib/chemicalml/cml/role/reactant_list.rb +13 -0
  143. data/lib/chemicalml/cml/role/reaction.rb +13 -0
  144. data/lib/chemicalml/cml/role/reaction_list.rb +13 -0
  145. data/lib/chemicalml/cml/role/scalar.rb +13 -0
  146. data/lib/chemicalml/cml/role/substance.rb +13 -0
  147. data/lib/chemicalml/cml/role/unit.rb +13 -0
  148. data/lib/chemicalml/cml/role/unit_list.rb +13 -0
  149. data/lib/chemicalml/cml/role/unit_type.rb +13 -0
  150. data/lib/chemicalml/cml/role/unit_type_list.rb +13 -0
  151. data/lib/chemicalml/cml/role.rb +54 -0
  152. data/lib/chemicalml/cml/scalar.rb +10 -0
  153. data/lib/chemicalml/cml/schema24/configuration.rb +19 -0
  154. data/lib/chemicalml/cml/schema24.rb +40 -0
  155. data/lib/chemicalml/cml/schema3/configuration.rb +17 -0
  156. data/lib/chemicalml/cml/schema3.rb +40 -0
  157. data/lib/chemicalml/cml/substance.rb +10 -0
  158. data/lib/chemicalml/cml/translator/value_translations.rb +265 -0
  159. data/lib/chemicalml/cml/translator.rb +233 -0
  160. data/lib/chemicalml/cml/unit.rb +10 -0
  161. data/lib/chemicalml/cml/unit_list.rb +10 -0
  162. data/lib/chemicalml/cml/unit_type.rb +10 -0
  163. data/lib/chemicalml/cml/unit_type_list.rb +10 -0
  164. data/lib/chemicalml/cml/visitable.rb +65 -0
  165. data/lib/chemicalml/cml/wire_class_macro.rb +45 -0
  166. data/lib/chemicalml/cml.rb +55 -0
  167. data/lib/chemicalml/context_configuration.rb +127 -0
  168. data/lib/chemicalml/convention/base.rb +48 -0
  169. data/lib/chemicalml/convention/compchem/constraints/compchem_module_must_contain_job_list.rb +33 -0
  170. data/lib/chemicalml/convention/compchem/constraints/job_must_contain_initialization.rb +38 -0
  171. data/lib/chemicalml/convention/compchem/constraints.rb +14 -0
  172. data/lib/chemicalml/convention/compchem.rb +28 -0
  173. data/lib/chemicalml/convention/constraint.rb +79 -0
  174. data/lib/chemicalml/convention/dictionary/constraints/entry_ids_unique_within_dictionary.rb +33 -0
  175. data/lib/chemicalml/convention/dictionary/constraints/entry_must_have_id_and_term.rb +34 -0
  176. data/lib/chemicalml/convention/dictionary/constraints.rb +14 -0
  177. data/lib/chemicalml/convention/dictionary.rb +28 -0
  178. data/lib/chemicalml/convention/molecular/constraints/atom_array_must_contain_atoms.rb +30 -0
  179. data/lib/chemicalml/convention/molecular/constraints/atom_ids_unique_within_molecule.rb +42 -0
  180. data/lib/chemicalml/convention/molecular/constraints/bond_must_reference_atoms_in_same_molecule.rb +54 -0
  181. data/lib/chemicalml/convention/molecular/constraints.rb +19 -0
  182. data/lib/chemicalml/convention/molecular.rb +29 -0
  183. data/lib/chemicalml/convention/registry.rb +50 -0
  184. data/lib/chemicalml/convention/unit_dictionary/constraints/unit_must_have_symbol_and_unit_type.rb +46 -0
  185. data/lib/chemicalml/convention/unit_dictionary/constraints.rb +12 -0
  186. data/lib/chemicalml/convention/unit_dictionary.rb +27 -0
  187. data/lib/chemicalml/convention/unit_type_dictionary/constraints/unit_type_must_have_id_and_name.rb +34 -0
  188. data/lib/chemicalml/convention/unit_type_dictionary/constraints.rb +12 -0
  189. data/lib/chemicalml/convention/unit_type_dictionary.rb +27 -0
  190. data/lib/chemicalml/convention/violation.rb +36 -0
  191. data/lib/chemicalml/convention.rb +39 -0
  192. data/lib/chemicalml/dictionary/entry.rb +59 -0
  193. data/lib/chemicalml/dictionary/enum.rb +51 -0
  194. data/lib/chemicalml/dictionary/link.rb +32 -0
  195. data/lib/chemicalml/dictionary/loader.rb +60 -0
  196. data/lib/chemicalml/dictionary/model.rb +64 -0
  197. data/lib/chemicalml/dictionary/registry.rb +75 -0
  198. data/lib/chemicalml/dictionary.rb +26 -0
  199. data/lib/chemicalml/errors.rb +7 -0
  200. data/lib/chemicalml/model/atom.rb +40 -0
  201. data/lib/chemicalml/model/bond.rb +33 -0
  202. data/lib/chemicalml/model/cml_array.rb +27 -0
  203. data/lib/chemicalml/model/cml_module.rb +45 -0
  204. data/lib/chemicalml/model/document.rb +29 -0
  205. data/lib/chemicalml/model/formula.rb +34 -0
  206. data/lib/chemicalml/model/identifier.rb +21 -0
  207. data/lib/chemicalml/model/label.rb +21 -0
  208. data/lib/chemicalml/model/matrix.rb +29 -0
  209. data/lib/chemicalml/model/metadata.rb +23 -0
  210. data/lib/chemicalml/model/metadata_list.rb +25 -0
  211. data/lib/chemicalml/model/molecule.rb +38 -0
  212. data/lib/chemicalml/model/name.rb +21 -0
  213. data/lib/chemicalml/model/node.rb +45 -0
  214. data/lib/chemicalml/model/parameter.rb +27 -0
  215. data/lib/chemicalml/model/parameter_list.rb +25 -0
  216. data/lib/chemicalml/model/product.rb +21 -0
  217. data/lib/chemicalml/model/product_list.rb +21 -0
  218. data/lib/chemicalml/model/property.rb +28 -0
  219. data/lib/chemicalml/model/property_list.rb +25 -0
  220. data/lib/chemicalml/model/reactant.rb +21 -0
  221. data/lib/chemicalml/model/reactant_list.rb +21 -0
  222. data/lib/chemicalml/model/reaction.rb +41 -0
  223. data/lib/chemicalml/model/reaction_list.rb +23 -0
  224. data/lib/chemicalml/model/scalar.rb +25 -0
  225. data/lib/chemicalml/model/substance.rb +25 -0
  226. data/lib/chemicalml/model.rb +38 -0
  227. data/lib/chemicalml/schema/definition.rb +41 -0
  228. data/lib/chemicalml/schema/registry.rb +30 -0
  229. data/lib/chemicalml/schema.rb +35 -0
  230. data/lib/chemicalml/version.rb +5 -0
  231. data/lib/chemicalml/versioned_parser.rb +43 -0
  232. data/lib/chemicalml.rb +47 -0
  233. data/lib/tasks/reference_fetch.rake +105 -0
  234. data/lib/tasks/scrape_fixtures.rake +133 -0
  235. data/reference-docs/conventions/compchem.md +141 -0
  236. data/reference-docs/conventions/dictionary.md +119 -0
  237. data/reference-docs/conventions/index.md +56 -0
  238. data/reference-docs/conventions/molecular.md +115 -0
  239. data/reference-docs/conventions/unit-dictionary.md +92 -0
  240. data/reference-docs/conventions/unitType-dictionary.md +51 -0
  241. data/reference-docs/dictionaries/compchem.md +335 -0
  242. data/reference-docs/dictionaries/index.md +52 -0
  243. data/reference-docs/examples/schema24-index.md +50 -0
  244. data/reference-docs/examples/schema3-compchem-index.md +14 -0
  245. data/reference-docs/examples/schema3-molecular-index.md +28 -0
  246. data/reference-docs/schemas/schema24/schema.xsd +10624 -0
  247. data/reference-docs/schemas/schema3/schema.xsd +11459 -0
  248. metadata +306 -0
@@ -0,0 +1,33 @@
1
+ # 16 — Schema24 convention coverage
2
+
3
+ **Status:** complete
4
+ **Depends on:** 10
5
+
6
+ ## Goal
7
+
8
+ All convention specs today build `Chemicalml::Cml::Document.new(...)`
9
+ which is an alias for Schema3. The constraints are written to accept
10
+ both schemas (after task 10, via role modules), but no spec verifies
11
+ Schema24 documents.
12
+
13
+ Add parallel examples in each convention spec that build Schema24
14
+ documents and verify the same violations fire.
15
+
16
+ ## Why
17
+
18
+ - Confidence that the role-module refactor (task 10) actually works.
19
+ - Catches regressions where a future edit accidentally only checks
20
+ Schema3.
21
+
22
+ ## Deliverables
23
+
24
+ - [ ] `spec/chemicalml/convention/convention_spec.rb` extended with
25
+ Schema24 equivalents of the molecular, compchem, and dictionary
26
+ constraint specs.
27
+ - [ ] A shared example helper to avoid duplicating the schema24 path
28
+ for every constraint.
29
+
30
+ ## Acceptance
31
+
32
+ - At least one Schema24-path example per convention.
33
+ - All examples pass.
@@ -0,0 +1,28 @@
1
+ # 17 — Final spec + lint pass
2
+
3
+ **Status:** complete
4
+ **Depends on:** all
5
+
6
+ ## Goal
7
+
8
+ Run the full suite, audit forbidden patterns, document final state.
9
+
10
+ ## Deliverables
11
+
12
+ - [ ] `bundle exec rspec spec/chemicalml/` green.
13
+ - [ ] `grep -rn 'require_relative' lib/` clean.
14
+ - [ ] `grep -rn '^require .chemicalml' lib/` clean.
15
+ - [ ] `grep -rn 'respond_to?' lib/` clean.
16
+ - [ ] `grep -rn 'instance_variable_set\|instance_variable_get' lib/`
17
+ clean.
18
+ - [ ] `grep -rn '[^a-zA-Z_\.]send[ ]*(' lib/` clean.
19
+ - [ ] `grep -rn 'is_a?(Chemicalml::Cml::Schema' lib/chemicalml/convention/`
20
+ clean.
21
+ - [ ] Both `Schema3::Configuration` and `Schema24::Configuration` are
22
+ under 20 lines.
23
+ - [ ] `TODO.align/README.round2.md` index shows every workstream
24
+ `complete`.
25
+
26
+ ## Acceptance
27
+
28
+ All of the above verified.
@@ -0,0 +1,27 @@
1
+ # TODO.align
2
+
3
+ Master plan for aligning the `chemicalml` gem's architecture with the
4
+ patterns established by the sibling `mml` gem
5
+ (https://github.com/plurimath/mml). Each file in this directory is one
6
+ workstream.
7
+
8
+ These tasks refactor the existing scaffolding so that schema versions,
9
+ namespaces, and round-trip validation follow the same idioms `mml` uses
10
+ for MathML 2/3/4.
11
+
12
+ | # | Workstream | Status | Depends on |
13
+ |---|---|---|---|
14
+ | 01 | [Cml::Base shared mixins](01-cml-base-mixins.md) | complete | — |
15
+ | 02 | [Real Schema3/Schema24 hierarchies](02-real-schema-hierarchies.md) | complete | 01 |
16
+ | 03 | [Configuration + lutaml_default_register](03-configuration.md) | complete | 02 |
17
+ | 04 | [Top-level parse / serialize entry points](04-parse-entry-points.md) | complete | 02, 03 |
18
+ | 05 | [Schema-aware Translator](05-schema-aware-translator.md) | complete | 02 |
19
+ | 06 | [Replace respond_to? with is_a?](06-no-respond-to.md) | complete | 02 |
20
+ | 07 | [Canon-based semantic XML comparison](07-canon-xml-comparison.md) | complete | — |
21
+ | 08 | [Expand fixtures corpus](08-expand-fixtures.md) | complete | 02 |
22
+ | 09 | [Final spec + lint pass](09-final-spec-lint.md) | complete | all |
23
+
24
+ **All workstreams complete.** 136 examples, 0 failures. Zero
25
+ `require_relative`, zero intra-library `require`, zero `respond_to?`,
26
+ zero `instance_variable_set`/`instance_variable_get`, zero private
27
+ `send` in `lib/`.
@@ -0,0 +1,24 @@
1
+ # TODO.align (round 2)
2
+
3
+ Architectural improvements identified after the round-1 alignment with
4
+ the `mml` patterns. Each file documents one focused workstream.
5
+
6
+ Round 1 (TODO.align/01–09) established the versioned class hierarchies,
7
+ configuration, and convention framework. Round 2 (TODO.align/10–17)
8
+ eliminates remaining OCP / DRY / SSOT violations.
9
+
10
+ | # | Workstream | Status | Depends on |
11
+ |---|---|---|---|
12
+ | 10 | [Role marker modules](10-role-marker-modules.md) | complete | — |
13
+ | 11 | [DRY Configuration registration](11-dry-configuration.md) | complete | — |
14
+ | 12 | [Wire class definition macro](12-wire-class-macro.md) | complete | 11 |
15
+ | 13 | [Wire XML namespace setup](13-wire-namespace.md) | complete | — |
16
+ | 14 | [Schema 2.4 correctness — drop `<module>`](14-schema24-correctness.md) | complete | 11 |
17
+ | 15 | [Canon-based fixture round-trip](15-canon-fixture-roundtrip.md) | complete | 13 |
18
+ | 16 | [Schema24 convention coverage](16-schema24-convention-coverage.md) | complete | 10 |
19
+ | 17 | [Final spec + lint pass](17-final-spec-lint.md) | complete | all |
20
+
21
+ **All workstreams complete.** 142 examples, 0 failures. Schema3 and
22
+ Schema24 configurations are 17 and 19 lines respectively. 66
23
+ boilerplate class files eliminated (33 per schema). Zero forbidden
24
+ patterns in `lib/`.
@@ -0,0 +1,43 @@
1
+ # 01 — Reference docs
2
+
3
+ **Status:** pending
4
+ **Depends on:** —
5
+
6
+ ## Goal
7
+
8
+ Pull every authoritative source document from xml-cml.org into
9
+ `reference-docs/` so the gem ships with its own canonical references.
10
+ These files are archival source — never delete or edit them.
11
+
12
+ ## Deliverables
13
+
14
+ - [ ] `reference-docs/cmllite.html` — the CMLLite paper
15
+ (https://www.xml-cml.org/schema/cmllite.html)
16
+ - [ ] `reference-docs/conventions/index.md` — the conventions landing page
17
+ (http://www.xml-cml.org/convention/)
18
+ - [ ] `reference-docs/conventions/molecular.md`
19
+ - [ ] `reference-docs/conventions/compchem.md`
20
+ - [ ] `reference-docs/conventions/dictionary.md`
21
+ - [ ] `reference-docs/conventions/unit-dictionary.md`
22
+ - [ ] `reference-docs/conventions/unitType-dictionary.md`
23
+ - [ ] `reference-docs/dictionaries/compchem.xml` — the live CompChem
24
+ dictionary at http://www.xml-cml.org/dictionary/compchem/
25
+ - [ ] `reference-docs/dictionaries/unit.xml` — SI/non-SI unit dictionary
26
+ - [ ] `reference-docs/dictionaries/unitType.xml` — unit-type dictionary
27
+ - [ ] A note at the top of each file pointing to its source URL and the
28
+ CC-BY-3.0 license
29
+
30
+ ## Implementation notes
31
+
32
+ Use `bundle exec rake reference:fetch` (added in
33
+ `lib/tasks/reference_fetch.rake`) — this downloads everything in one
34
+ shot, skips files that already exist, and prints a summary. Do **not**
35
+ commit generated files without reviewing them; some upstream URLs may
36
+ redirect or 404 — log those and move on, don't fail the whole task.
37
+
38
+ ## Acceptance
39
+
40
+ - All listed files exist under `reference-docs/`.
41
+ - Each file starts with a one-line `<!-- Source: URL, CC-BY-3.0 -->` comment
42
+ (or Markdown equivalent).
43
+ - `bundle exec rake reference:fetch` is idempotent.
@@ -0,0 +1,70 @@
1
+ # 02 — Schema-versioned wire model
2
+
3
+ **Status:** pending
4
+ **Depends on:** 01
5
+
6
+ ## Goal
7
+
8
+ Support both CML schema 2.4 and schema 3 as first-class wire formats.
9
+ The two XSDs (already in `reference-docs/schemas/`) are the source of
10
+ truth for element/attribute names and content models.
11
+
12
+ ## Why split the namespace
13
+
14
+ Schema 3 loosened the content model of schema 2.4 — many elements
15
+ gained optional children, some attribute types changed, and a few new
16
+ elements were added. Conflating the two into one class set:
17
+
18
+ - forces every user to pay schema-3-only validation cost,
19
+ - makes it impossible to express "this element only exists in schema 3",
20
+ - hides which version a round-trip targets.
21
+
22
+ Keeping them separate (Schema24 / Schema3 namespaces) gives users an
23
+ explicit choice and keeps each class honest about its own version.
24
+
25
+ ## Deliverables
26
+
27
+ - [ ] `Chemicalml::Schema` registry: maps version symbols (`:schema24`,
28
+ `:schema3`) to their XSD path, namespace, and the wire-class
29
+ namespace.
30
+ - [ ] `Chemicalml::Cml::Schema3::*` — schema 3 wire classes
31
+ - [ ] `Chemicalml::Cml::Schema24::*` — schema 2.4 wire classes
32
+ - [ ] A shared mixin `Chemicalml::Cml::Common` for the elements that
33
+ have **identical** shapes in both versions (most of them —
34
+ schema 3 is largely a superset). The mixin carries `attribute`
35
+ declarations and the `xml do ... end` mapping block; the
36
+ schema-specific class includes it.
37
+ - [ ] For elements that differ between versions (e.g. `molecule`'s
38
+ content model), each version gets its own dedicated class — no
39
+ shared mixin.
40
+ - [ ] Update `Chemicalml::Cml::Translator` so it accepts a
41
+ `schema:` keyword and dispatches to the right wire class set.
42
+
43
+ ## Scope of classes to add
44
+
45
+ Driven by what the conventions and example fixtures need:
46
+
47
+ Common (likely shared via mixin): `atom`, `atomArray`, `bond`,
48
+ `bondArray`, `name`, `identifier`, `formula`, `property`, `parameter`,
49
+ `scalar`, `array`, `matrix`, `label`, `module`, `list`, `reaction`,
50
+ `reactantList`, `reactant`, `productList`, `product`, `reactionList`,
51
+ `substance`, `substanceList`, `spectator`, `spectatorList`,
52
+ `parameterList`, `propertyList`, `metadata`, `metadataList`,
53
+ `atomParity`, `bondStereo`, `atomSet`, `bondSet`, `torsion`, `angle`,
54
+ `length`, `zMatrix`, `crystal`, `lattice`, `latticeVector`,
55
+ `cellParameter`, `eigenvector`.
56
+
57
+ Differences to honor:
58
+
59
+ - Schema 2.4: stricter content models (e.g. `<molecule>` cannot have
60
+ arbitrary child modules).
61
+ - Schema 3: introduced `<module>` as a generic grouping container;
62
+ loosened `<cml>` to allow any top-level CML element.
63
+
64
+ ## Acceptance
65
+
66
+ - All schema-versioned wire classes are loadable.
67
+ - Both schema XSDs round-trip the example fixtures in their respective
68
+ `spec/fixtures/schema{24,3}/` directories.
69
+ - No `require_relative` anywhere — all loading via autoload in
70
+ `lib/chemicalml/cml/schema3.rb`, `schema24.rb`, etc.
@@ -0,0 +1,52 @@
1
+ # 03 — Fixtures scrape
2
+
3
+ **Status:** pending
4
+ **Depends on:** 01
5
+
6
+ ## Goal
7
+
8
+ Pull every example CML document from xml-cml.org/examples/ into
9
+ `spec/fixtures/`, organized by schema version and category. These are
10
+ the canonical test corpus for round-trip and convention-conformance
11
+ specs.
12
+
13
+ ## Source URLs
14
+
15
+ - http://www.xml-cml.org/examples/schema3/molecular/ — schema 3 molecular
16
+ - http://www.xml-cml.org/examples/schema3/compchem/ — schema 3 compchem
17
+ - http://www.xml-cml.org/examples/schema24/ — schema 2.4
18
+
19
+ Each landing page is an HTML index linking to per-example `.html`
20
+ viewer pages that themselves embed the raw CML. The scraper has to
21
+ walk the index, fetch each example page, and extract the CML block.
22
+
23
+ ## Deliverables
24
+
25
+ - [ ] `lib/tasks/scrape_fixtures.rake` exposing
26
+ `bundle exec rake fixtures:scrape`. Idempotent — skips files that
27
+ already exist.
28
+ - [ ] `spec/fixtures/schema3/molecular/*.cml`
29
+ - [ ] `spec/fixtures/schema3/compchem/*.cml`
30
+ - [ ] `spec/fixtures/schema24/*.cml`
31
+ - [ ] A smoke spec `spec/chemicalml/fixtures_round_trip_spec.rb` that
32
+ loads every `.cml` fixture, runs
33
+ `Chemicalml::Cml::Schema3::Document.from_xml(...)`, and asserts
34
+ `to_xml` produces a document the parser can re-read.
35
+
36
+ ## Implementation notes
37
+
38
+ - Use `Net::HTTP` directly — no extra gem dependency. Honour a 1-second
39
+ delay between requests to be polite.
40
+ - File names: derive from the example title, slugged to
41
+ `[a-z0-9_-]+`. Preserve uniqueness with a numeric suffix if needed.
42
+ - The example index pages render titles like "minimal molecule 1" —
43
+ slug those to `minimal_molecule_1.cml`.
44
+ - If a fetch fails (404, redirect to a different host), record the URL
45
+ in `spec/fixtures/.scrape-log` and continue — don't abort the run.
46
+
47
+ ## Acceptance
48
+
49
+ - All three fixture directories exist with the expected example count
50
+ (molecular ~36, compchem ~5, schema24 varies).
51
+ - The round-trip smoke spec is green.
52
+ - Re-running `rake fixtures:scrape` makes no changes.
@@ -0,0 +1,69 @@
1
+ # 04 — Convention framework
2
+
3
+ **Status:** pending
4
+ **Depends on:** 02
5
+
6
+ ## Goal
7
+
8
+ Model the five conventions CML publishes, plus a generic
9
+ constraint-evaluation framework so adding a new rule is a registration,
10
+ not an edit.
11
+
12
+ ## Conventions
13
+
14
+ Each is identified by its namespace URI and the QName used in the
15
+ `convention=` attribute:
16
+
17
+ | Convention | QName | Namespace URI |
18
+ |-------------------|---------------------------|---------------------------------------------------|
19
+ | Molecular | `convention:molecular` | http://www.xml-cml.org/convention/molecular |
20
+ | CompChem | `convention:compchem` | http://www.xml-cml.org/convention/compchem |
21
+ | Dictionary | `convention:dictionary` | http://www.xml-cml.org/convention/dictionary |
22
+ | Unit dictionary | `convention:unit-dictionary` | http://www.xml-cml.org/convention/unit-dictionary |
23
+ | UnitType dictionary | `convention:unitType-dictionary` | http://www.xml-cml.org/convention/unitType-dictionary |
24
+
25
+ ## Deliverables
26
+
27
+ - [ ] `Chemicalml::Convention::Base` — abstract base. Carries the
28
+ namespace URI, a human-readable name, and a registry of
29
+ constraint classes.
30
+ - [ ] `Chemicalml::Convention::Registry` — maps QName → convention
31
+ instance. `Convention::Registry.lookup("convention:molecular")`
32
+ returns the convention.
33
+ - [ ] `Chemicalml::Convention::Constraint` — abstract base for a single
34
+ rule. Each constraint implements `check(node, context)` returning
35
+ either `nil` (pass) or a `Violation` instance.
36
+ - [ ] `Chemicalml::Convention::Violation` — value object with `path`,
37
+ `message`, `severity` (`:error` / `:warning`).
38
+ - [ ] `Chemicalml::Convention::Molecular` — preloads every constraint
39
+ listed in the molecular convention spec (atom ids unique, bond
40
+ refs in scope, etc.).
41
+ - [ ] `Chemicalml::Convention::Compchem` — constraints around the
42
+ `module` / `jobList` / `job` / `initialization` / `calculation`
43
+ / `finalization` / `environment` structure.
44
+ - [ ] `Chemicalml::Convention::Dictionary`, `UnitDictionary`,
45
+ `UnitTypeDictionary` — dictionary-element constraints (entry id
46
+ uniqueness, required `term`, etc.).
47
+ - [ ] `Chemicalml::Convention.validate(document, qname:)` — runs every
48
+ constraint for the named convention and returns the violation
49
+ list.
50
+
51
+ ## Design constraints (non-negotiable)
52
+
53
+ - New constraint = **register a class**, never edit a switch statement.
54
+ The molecular convention's constraints live in
55
+ `lib/chemicalml/convention/molecular/constraints/`, one class per
56
+ file, registered in `lib/chemicalml/convention/molecular.rb`.
57
+ - The framework never knows about specific constraints — it only
58
+ iterates whatever the convention registered.
59
+ - Constraints may walk the tree using `Node#accept(visitor)` from the
60
+ canonical model — they don't have to re-implement traversal.
61
+
62
+ ## Acceptance
63
+
64
+ - All five conventions are registered and return non-empty
65
+ constraint sets.
66
+ - A molecular-convention-invalid document (e.g. duplicate atom ids)
67
+ produces at least one `Violation` with `severity: :error`.
68
+ - Specs cover at least one positive and one negative case per
69
+ convention.
@@ -0,0 +1,77 @@
1
+ # 05 — Dictionary layer
2
+
3
+ **Status:** pending
4
+ **Depends on:** 04
5
+
6
+ ## Goal
7
+
8
+ Model the CML dictionary convention and ship built-in dictionaries as
9
+ YAML, with a typed entry schema. Dictionaries must be queryable by id
10
+ and by QName prefix.
11
+
12
+ ## YAML entry schema
13
+
14
+ Each entry is one YAML document under the dictionary's namespace:
15
+
16
+ ```yaml
17
+ ---
18
+ namespace: http://www.xml-cml.org/dictionary/compchem/
19
+ prefix: compchem
20
+ title: Computational Chemistry Dictionary
21
+ description: |
22
+ Terms used by the CompChem convention.
23
+ entries:
24
+ - id: jobList
25
+ term: Job List
26
+ definition: |
27
+ A list of sequential computational jobs.
28
+ description: |
29
+ <p>A jobList...</p> # xhtml content as a string
30
+ data_type: xsd:string # optional; QName
31
+ unit_type: unitType:none # required; QName
32
+ units: unit:none # optional; QName
33
+ enum:
34
+ kind: open # open | closed
35
+ values: [] # populated only when kind: closed
36
+ links:
37
+ - rel: seeAlso
38
+ href: http://example.org/foo
39
+ source_code: |
40
+ <code>...</code> # optional xhtml/code block
41
+ ```
42
+
43
+ The dictionary itself is the file; the schema is one entry per
44
+ record under `entries:`.
45
+
46
+ ## Deliverables
47
+
48
+ - [ ] `Chemicalml::Dictionary` — top-level model: `namespace`,
49
+ `prefix`, `title`, `description`, `entries` (collection).
50
+ Built on `Lutaml::Model::Serializable` with YAML mapping.
51
+ - [ ] `Chemicalml::Dictionary::Entry` — typed model with all the
52
+ fields above. `enum` is itself a small model with `kind:` and
53
+ `values:`.
54
+ - [ ] `Chemicalml::Dictionary::Link` — `rel`, `href`, optional `title`.
55
+ - [ ] `Chemicalml::Dictionary::Registry` — loads every `.yaml` under
56
+ `data/dictionaries/`, indexes by both `prefix:` and `namespace:`.
57
+ `Registry.lookup("compchem:totalEnergy")` returns the entry.
58
+ - [ ] Built-in YAML files under `data/dictionaries/`:
59
+ `compchem.yaml`, `unit.yaml`, `unit_type.yaml`, `cml.yaml`
60
+ (core CML terms referenced from the dictionaries page).
61
+ - [ ] `Chemicalml::Dictionary.load(name)` — convenience loader.
62
+
63
+ ## Design constraints
64
+
65
+ - Entries are loaded from YAML, never hand-coded in Ruby.
66
+ - The model classes do NOT serialize back to YAML by hand — they
67
+ use lutaml-model's YAML adapter via `yaml do ... end` mapping blocks.
68
+ - No `require_relative` in any of these files.
69
+
70
+ ## Acceptance
71
+
72
+ - `Chemicalml::Dictionary.load("compchem")` returns a populated
73
+ `Dictionary` with at least the entries cited in the compchem
74
+ convention (`jobList`, `job`, `initialization`, `calculation`,
75
+ `finalization`, `environment`, `totalEnergy`, etc.).
76
+ - The registry resolves QNames of the form `prefix:id` correctly.
77
+ - Round-trip: load YAML → model → re-serialize to YAML → reload → equal.
@@ -0,0 +1,60 @@
1
+ # 06 — Extend canonical model + translator
2
+
3
+ **Status:** pending
4
+ **Depends on:** 02, 05
5
+
6
+ ## Goal
7
+
8
+ The canonical `Chemicalml::Model` and the `Cml::Translator` currently
9
+ cover the molecule / atom / bond / reaction subset. The full CML
10
+ conventions need formula, property, parameter, scalar/array/matrix,
11
+ module (the compchem container), label, and metadata. Bring both up to
12
+ speed together so round-trip coverage matches the example fixtures.
13
+
14
+ ## Deliverables
15
+
16
+ ### Canonical model additions (`Chemicalml::Model::*`)
17
+
18
+ - [ ] `Formula` — `concise`, `inline`, optional `atom_array`, `count`
19
+ - [ ] `Property` — wraps a single value container, has `dict_ref`, `title`
20
+ - [ ] `Parameter` — same shape as Property but used for inputs
21
+ - [ ] `Scalar` — `value`, `data_type`, `units`
22
+ - [ ] `Array` — `values` (split string), `data_type`, `units`, `size`
23
+ - [ ] `Matrix` — `values`, `rows`, `columns`, `data_type`, `units`
24
+ - [ ] `Label` — `value`, `dict_ref`
25
+ - [ ] `Module` — generic grouping container, `dict_ref`, `role`, ordered children
26
+ - [ ] `Metadata` — `name`, `content`, `convention`
27
+ - [ ] `PropertyList`, `ParameterList`, `MetadataList` — collection wrappers
28
+
29
+ ### CML wire additions (`Chemicalml::Cml::*` or per-schema)
30
+
31
+ - [ ] Same set as above, one lutaml-model class each, with the right
32
+ XML element name and attribute names from the XSDs.
33
+
34
+ ### Translator updates
35
+
36
+ - [ ] Add a translation rule for every new model class. Each rule is a
37
+ pair of methods on `Translator` following the existing
38
+ `*_to_canonical` / `*_from_canonical` pattern.
39
+ - [ ] Where the wire name differs from the Ruby name, the CML class's
40
+ `xml do ... end` block carries the rename (e.g.
41
+ `map_attribute "elementType", to: :element_type`). The translator
42
+ doesn't do key-swapping.
43
+ - [ ] Add a `value_attributes` implementation for every new canonical
44
+ class so equality and visitor traversal work.
45
+
46
+ ## Design constraints
47
+
48
+ - One concern per file (MECE). Adding `Scalar` =
49
+ `model/scalar.rb` + `cml/scalar.rb` (or per-schema) + translator
50
+ rule + spec. Don't bundle several elements into one file.
51
+ - The translator is the only class that imports both namespaces.
52
+ - No `def to_h` / `def from_h` / `def to_xml` / `def from_xml` on any
53
+ model or wire class.
54
+
55
+ ## Acceptance
56
+
57
+ - Every example fixture round-trips through
58
+ `Document.from_xml(...).to_xml` and re-parses to an equal document.
59
+ - New model classes have specs covering construction, equality, and
60
+ translator round-trip.
@@ -0,0 +1,42 @@
1
+ # 07 — Attribution & notices
2
+
3
+ **Status:** pending
4
+ **Depends on:** —
5
+
6
+ ## Goal
7
+
8
+ The CML conventions, schema, examples, and dictionaries are published
9
+ by xml-cml.org under a Creative Commons Attribution 3.0 (CC-BY-3.0)
10
+ license. The gem redistributes derived works (model classes,
11
+ dictionaries as YAML, scraped fixtures, archived convention specs).
12
+ Proper attribution is both legally required and a basic courtesy to
13
+ the people who built CML.
14
+
15
+ ## Deliverables
16
+
17
+ - [ ] `NOTICES.adoc` at the repo root crediting:
18
+ - Peter Murray-Rust, Joe Townsend, Sam Adams, and the wider
19
+ CML community at the University of Cambridge.
20
+ - Weerapong Phadungsukanan, Jens Thomas, Nick England,
21
+ Daniel Lowe, Hannah Barjat, Alex Wade.
22
+ - The xml-cml.org project and the Chemical Markup Language
23
+ community.
24
+ Each credit lists what they contributed (convention editors,
25
+ dictionary authors, schema maintainers, etc.).
26
+ - [ ] A "Third-party content" section in `NOTICES.adoc` listing every
27
+ file under `reference-docs/` and `spec/fixtures/` that derives
28
+ from xml-cml.org, with the upstream URL and the CC-BY-3.0
29
+ license notice.
30
+ - [ ] A short "Acknowledgements" section in `README.adoc` mirroring
31
+ the above.
32
+ - [ ] Each archived reference doc keeps its own one-line source-and-
33
+ license attribution at the top (added when fetched, see
34
+ `01-reference-docs.md`).
35
+
36
+ ## Acceptance
37
+
38
+ - `NOTICES.adoc` exists, names every contributor surfaced in the
39
+ convention pages, and reproduces the CC-BY-3.0 notice.
40
+ - README.adoc has a visible acknowledgements section.
41
+ - `git ls-files reference-docs spec/fixtures | wc -l` non-zero (i.e.,
42
+ we actually redistribute some upstream content).
@@ -0,0 +1,48 @@
1
+ # 08 — Autoload-only loading
2
+
3
+ **Status:** pending
4
+ **Depends on:** —
5
+
6
+ ## Goal
7
+
8
+ The user's global rule (and CLAUDE.md) forbid `require_relative` and
9
+ intra-library `require` inside `lib/`. The current codebase still uses
10
+ them. Migrate every occurrence to Ruby `autoload` declared in the
11
+ immediate parent namespace's file.
12
+
13
+ ## Files to fix
14
+
15
+ The following files use `require_relative` or intra-library `require`
16
+ for code that lives inside this gem:
17
+
18
+ - `lib/chemicalml/model/document.rb`
19
+ - `lib/chemicalml/model/reaction.rb`
20
+ - `lib/chemicalml/model/reaction_list.rb`
21
+ - `lib/chemicalml/model/reactant.rb`
22
+ - `lib/chemicalml/model/reactant_list.rb`
23
+ - `lib/chemicalml/model/product.rb`
24
+ - `lib/chemicalml/model/product_list.rb`
25
+ - `lib/chemicalml/cml/translator.rb` (imports both Model and Cml — this
26
+ one is legitimate because Translator is the *adapter* between the two
27
+ namespaces; verify it still works via autoload, then drop the explicit
28
+ requires).
29
+ - `chemicalml.gemspec` (`require_relative "lib/chemicalml/version"`) —
30
+ this is the *only* legitimate use; gemspecs run before the gem is
31
+ loaded, so they cannot use autoload. Leave this one alone.
32
+
33
+ ## Deliverables
34
+
35
+ - [ ] Audit `lib/` for every `require_relative` and every `require`
36
+ whose target is a file inside `lib/chemicalml/`.
37
+ - [ ] Replace each with an `autoload` declaration in the immediate
38
+ parent namespace's file. If the parent namespace file doesn't
39
+ exist, create it.
40
+ - [ ] Confirm the spec suite still passes (`bundle exec rspec`).
41
+ - [ ] Add a RuboCop `Style/RequireRelative`-style guard to
42
+ `.rubocop.yml` (or a custom cop) so this can't regress.
43
+
44
+ ## Acceptance
45
+
46
+ - `grep -rn 'require_relative' lib/` returns only the gemspec line.
47
+ - `grep -rn "^require ['\"]chemicalml/" lib/` returns nothing.
48
+ - Full spec suite green.
@@ -0,0 +1,38 @@
1
+ # 09 — Spec coverage
2
+
3
+ **Status:** pending
4
+ **Depends on:** 02, 04, 05, 06
5
+
6
+ ## Goal
7
+
8
+ Every public method has specs. Every fixture round-trips. Every
9
+ convention has at least one passing and one failing case. No `double`.
10
+
11
+ ## Deliverables
12
+
13
+ - [ ] `spec/chemicalml/cml/schema3/*_spec.rb` — one per wire class
14
+ - [ ] `spec/chemicalml/cml/schema24/*_spec.rb` — one per wire class
15
+ - [ ] `spec/chemicalml/model/*_spec.rb` — one per canonical class
16
+ - [ ] `spec/chemicalml/cml/translator_spec.rb` — extended to cover the
17
+ new model classes added in `06-extend-canonical-translator.md`
18
+ - [ ] `spec/chemicalml/convention/*_spec.rb` — one per convention,
19
+ positive and negative paths
20
+ - [ ] `spec/chemicalml/dictionary_spec.rb` — registry lookup,
21
+ round-trip YAML → model → YAML
22
+ - [ ] `spec/chemicalml/fixtures_round_trip_spec.rb` — iterates every
23
+ `.cml` file under `spec/fixtures/`, parses, re-serializes,
24
+ re-parses, asserts equality
25
+
26
+ ## Constraints
27
+
28
+ - Never `double`. Use real instances or `Struct.new`.
29
+ - Specs assert on **state**, not on "should have received" method-call
30
+ counts.
31
+ - Specs live under `spec/chemicalml/`, mirroring `lib/chemicalml/`.
32
+
33
+ ## Acceptance
34
+
35
+ - `bundle exec rspec` is green.
36
+ - `bundle exec rspec --profile 10` shows no examples taking a
37
+ surprising amount of time (sanity check — no accidental network or
38
+ file I/O in hot paths).
@@ -0,0 +1,23 @@
1
+ # TODO.cml-full
2
+
3
+ Master plan for the full CML implementation of the `chemicalml` gem.
4
+
5
+ Each file in this directory is one workstream. Numbering is the
6
+ suggested implementation order; later tasks depend on earlier ones
7
+ where noted.
8
+
9
+ | # | Workstream | Status | Depends on |
10
+ |---|---|---|---|
11
+ | 01 | [Reference docs](01-reference-docs.md) — fetch & archive cmllite, conventions, dictionaries | pending | — |
12
+ | 02 | [Schema-versioned wire model](02-schema-versioned-model.md) — split Cml into Schema3 / Schema24 | pending | 01 |
13
+ | 03 | [Fixtures scrape](03-fixtures-scrape.md) — pull all example CML into `spec/fixtures/` | pending | 01 |
14
+ | 04 | [Convention framework](04-convention-framework.md) — registry + 5 conventions | pending | 02 |
15
+ | 05 | [Dictionary layer](05-dictionary-layer.md) — model + YAML store + entries | pending | 04 |
16
+ | 06 | [Extend canonical model + translator](06-extend-canonical-translator.md) — formula/property/parameter/scalar/array/matrix/module/label | pending | 02, 05 |
17
+ | 07 | [Attribution & notices](07-attribution-notices.md) — credit xml-cml.org, CC-BY-3.0 | pending | — |
18
+ | 08 | [Autoload-only loading](08-autoload-only.md) — remove all `require_relative` from lib/ | pending | — |
19
+ | 09 | [Spec coverage](09-spec-coverage.md) — every public method, every fixture round-trip | pending | 02, 04, 05, 06 |
20
+
21
+ When picking up work, claim the lowest-numbered pending task and update
22
+ its status line. Mark the workstream complete in this index once all
23
+ sub-items are done.