OperonDBS 0.7.2__tar.gz → 0.8.0__tar.gz

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 (297) hide show
  1. {operondbs-0.7.2 → operondbs-0.8.0}/.github/workflows/publish.yml +2 -1
  2. operondbs-0.8.0/.github/workflows/test.yml +51 -0
  3. {operondbs-0.7.2 → operondbs-0.8.0}/.gitignore +6 -1
  4. {operondbs-0.7.2 → operondbs-0.8.0}/AGENTS.md +46 -14
  5. {operondbs-0.7.2 → operondbs-0.8.0}/MANIFEST.in +2 -1
  6. {operondbs-0.7.2 → operondbs-0.8.0}/OperonDBS.egg-info/PKG-INFO +9 -11
  7. {operondbs-0.7.2 → operondbs-0.8.0}/OperonDBS.egg-info/SOURCES.txt +41 -3
  8. {operondbs-0.7.2 → operondbs-0.8.0}/OperonDBS.egg-info/requires.txt +7 -4
  9. {operondbs-0.7.2 → operondbs-0.8.0}/PKG-INFO +9 -11
  10. {operondbs-0.7.2 → operondbs-0.8.0}/docs/conf.py +1 -1
  11. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/extensibility.md +2 -2
  12. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/external-analysis.md +12 -6
  13. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/files-and-storage.md +4 -4
  14. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/metadata-and-data-model.md +1 -12
  15. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/overview.md +23 -4
  16. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/qc-and-rules.md +2 -0
  17. operondbs-0.8.0/docs/en/contributor/development-testing.md +100 -0
  18. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/contributor/repository-guide.md +2 -6
  19. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/getting-started/first-project.md +29 -27
  20. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/getting-started/quickstart.md +1 -9
  21. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/backup-migration.md +4 -16
  22. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/curation-lifecycle.md +1 -1
  23. operondbs-0.8.0/docs/en/guides/external-analysis.md +328 -0
  24. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/file-archiving.md +2 -13
  25. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/index.md +4 -0
  26. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/metadata-import.md +3 -1
  27. operondbs-0.8.0/docs/en/guides/phylogenetics-workflow.md +202 -0
  28. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/qc-profiles.md +68 -0
  29. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/remote-execution.md +1 -1
  30. operondbs-0.8.0/docs/en/guides/remote-first.md +138 -0
  31. operondbs-0.8.0/docs/en/guides/remote-storage.md +70 -0
  32. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/index.md +2 -7
  33. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/operations/database-compatibility.md +5 -1
  34. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/operations/qc-performance.md +6 -3
  35. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/overview.md +1 -11
  36. operondbs-0.8.0/docs/en/reference/behaviors-and-limitations.md +295 -0
  37. operondbs-0.8.0/docs/en/reference/cli-analysis.md +213 -0
  38. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/cli-decisions-reports.md +33 -7
  39. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/cli-files-qc.md +37 -3
  40. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/cli-project-metadata.md +45 -0
  41. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/cli-remote.md +3 -2
  42. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/cli-taxonomy-lifecycle-admin.md +2 -0
  43. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/cli-workflow.md +24 -3
  44. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/data-model.md +30 -17
  45. operondbs-0.8.0/docs/en/reference/recipe-fields.md +449 -0
  46. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/recipe-overview.md +6 -4
  47. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/recipe-parsers-examples.md +164 -8
  48. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/extensibility.md +7 -2
  49. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/external-analysis.md +39 -16
  50. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/files-and-storage.md +9 -11
  51. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/index.md +1 -1
  52. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/metadata-and-data-model.md +4 -31
  53. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/overview.md +23 -4
  54. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/qc-and-rules.md +5 -0
  55. operondbs-0.8.0/docs/zh/contributor/development-testing.md +107 -0
  56. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/contributor/repository-guide.md +2 -6
  57. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/getting-started/first-project.md +38 -58
  58. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/getting-started/quickstart.md +5 -11
  59. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/backup-migration.md +7 -14
  60. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/curation-lifecycle.md +4 -2
  61. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/external-analysis.md +127 -94
  62. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/file-archiving.md +7 -16
  63. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/index.md +4 -0
  64. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/metadata-import.md +7 -6
  65. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/ncbi-datasets.md +6 -6
  66. operondbs-0.8.0/docs/zh/guides/phylogenetics-workflow.md +244 -0
  67. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/qc-profiles.md +93 -0
  68. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/remote-execution.md +10 -3
  69. operondbs-0.8.0/docs/zh/guides/remote-first.md +169 -0
  70. operondbs-0.8.0/docs/zh/guides/remote-storage.md +82 -0
  71. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/taxonomy-coverage.md +5 -3
  72. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/index.md +2 -7
  73. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/operations/database-compatibility.md +19 -2
  74. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/operations/qc-performance.md +10 -5
  75. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/overview.md +2 -12
  76. operondbs-0.8.0/docs/zh/reference/behaviors-and-limitations.md +292 -0
  77. operondbs-0.8.0/docs/zh/reference/cli-analysis.md +326 -0
  78. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/cli-decisions-reports.md +63 -9
  79. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/cli-files-qc.md +50 -8
  80. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/cli-project-metadata.md +45 -0
  81. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/cli-remote.md +8 -1
  82. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/cli-taxonomy-lifecycle-admin.md +4 -0
  83. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/cli-workflow.md +16 -2
  84. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/data-model.md +60 -40
  85. operondbs-0.8.0/docs/zh/reference/recipe-fields.md +531 -0
  86. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/recipe-overview.md +9 -4
  87. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/recipe-parsers-examples.md +209 -7
  88. {operondbs-0.7.2 → operondbs-0.8.0}/operon/adapters/ncbi_datasets.py +18 -18
  89. operondbs-0.8.0/operon/adapters/timetree.py +377 -0
  90. operondbs-0.8.0/operon/classify.py +519 -0
  91. {operondbs-0.7.2 → operondbs-0.8.0}/operon/cli.py +641 -31
  92. {operondbs-0.7.2 → operondbs-0.8.0}/operon/database.py +175 -31
  93. {operondbs-0.7.2 → operondbs-0.8.0}/operon/demo.py +1 -1
  94. {operondbs-0.7.2 → operondbs-0.8.0}/operon/entity_view.py +15 -0
  95. operondbs-0.8.0/operon/environment.py +222 -0
  96. operondbs-0.8.0/operon/environment_capture.py +261 -0
  97. {operondbs-0.7.2 → operondbs-0.8.0}/operon/errors.py +9 -0
  98. {operondbs-0.7.2 → operondbs-0.8.0}/operon/execution.py +30 -10
  99. {operondbs-0.7.2 → operondbs-0.8.0}/operon/export.py +25 -0
  100. operondbs-0.8.0/operon/fanout.py +378 -0
  101. {operondbs-0.7.2/operon/qc_module → operondbs-0.8.0/operon/qc}/__init__.py +92 -86
  102. operondbs-0.8.0/operon/qc/_alignment.pyx +185 -0
  103. operondbs-0.8.0/operon/qc/alignment.py +176 -0
  104. operondbs-0.8.0/operon/qc/measure.py +239 -0
  105. {operondbs-0.7.2 → operondbs-0.8.0}/operon/remotes.py +90 -36
  106. operondbs-0.8.0/operon/sequence_tools.py +477 -0
  107. {operondbs-0.7.2 → operondbs-0.8.0}/operon/taxonomy.py +2 -2
  108. operondbs-0.8.0/operon/timetree.py +489 -0
  109. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tools.py +881 -54
  110. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/actions.py +1 -1
  111. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/data.py +21 -3
  112. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/common.py +14 -4
  113. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/config.py +11 -5
  114. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/import_wizard.py +3 -3
  115. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/publish.py +2 -2
  116. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/runs.py +1 -0
  117. {operondbs-0.7.2 → operondbs-0.8.0}/operon/workflow.py +153 -8
  118. {operondbs-0.7.2 → operondbs-0.8.0}/pyproject.toml +18 -14
  119. {operondbs-0.7.2 → operondbs-0.8.0}/setup.py +4 -1
  120. {operondbs-0.7.2 → operondbs-0.8.0}/tests/compatibility/test_python_support.py +24 -3
  121. operondbs-0.8.0/tests/integration/test_analysis_tools.py +1064 -0
  122. operondbs-0.8.0/tests/integration/test_classify_sequences.py +206 -0
  123. operondbs-0.8.0/tests/integration/test_fanout_analyze.py +144 -0
  124. operondbs-0.8.0/tests/integration/test_python_packaging.py +169 -0
  125. operondbs-0.8.0/tests/integration/test_qc_remote_measure.py +69 -0
  126. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/test_taxonomy_coverage.py +1 -1
  127. {operondbs-0.7.2 → operondbs-0.8.0}/tests/regression/test_correctness.py +1 -1
  128. operondbs-0.8.0/tests/regression/test_cython_alignment_parity.py +151 -0
  129. {operondbs-0.7.2 → operondbs-0.8.0}/tests/regression/test_cython_parser_parity.py +4 -4
  130. {operondbs-0.7.2 → operondbs-0.8.0}/tests/regression/test_parser_semantics.py +2 -2
  131. operondbs-0.8.0/tests/unit/test_alignment_qc.py +285 -0
  132. operondbs-0.8.0/tests/unit/test_classify.py +584 -0
  133. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_cli_edges.py +14 -10
  134. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_config_workflow_edges.py +50 -9
  135. operondbs-0.8.0/tests/unit/test_core_coverage_more.py +2030 -0
  136. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_coverage_edges.py +0 -1
  137. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_database_edges_more.py +0 -8
  138. operondbs-0.8.0/tests/unit/test_environment.py +296 -0
  139. operondbs-0.8.0/tests/unit/test_environment_capture.py +326 -0
  140. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_execution.py +33 -39
  141. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_execution_edges.py +36 -27
  142. operondbs-0.8.0/tests/unit/test_fanout.py +296 -0
  143. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_files_edges.py +23 -3
  144. operondbs-0.8.0/tests/unit/test_follow.py +265 -0
  145. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_import_backup_show.py +0 -22
  146. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_lifecycle.py +3 -17
  147. operondbs-0.8.0/tests/unit/test_misc_coverage_more.py +1548 -0
  148. operondbs-0.8.0/tests/unit/test_ncbi_coverage_gaps.py +1517 -0
  149. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_parser_edge_paths.py +1 -1
  150. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_qc_and_rules.py +8 -18
  151. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_qc_edges_more.py +15 -6
  152. operondbs-0.8.0/tests/unit/test_qc_measure.py +434 -0
  153. operondbs-0.8.0/tests/unit/test_qc_remote_only.py +141 -0
  154. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_recipe_history.py +0 -12
  155. operondbs-0.8.0/tests/unit/test_remote_taxonomy_more.py +1504 -0
  156. operondbs-0.8.0/tests/unit/test_remote_transport_edges.py +254 -0
  157. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_remotes.py +12 -20
  158. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_remotes_edges.py +2 -1
  159. operondbs-0.8.0/tests/unit/test_schema_2_10.py +79 -0
  160. operondbs-0.8.0/tests/unit/test_schema_2_11.py +62 -0
  161. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_schema_2_9.py +1 -2
  162. operondbs-0.8.0/tests/unit/test_sequence_tools.py +350 -0
  163. operondbs-0.8.0/tests/unit/test_sequences.py +168 -0
  164. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_support_edges.py +13 -2
  165. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_taxonomy_edges.py +18 -30
  166. operondbs-0.8.0/tests/unit/test_timetree.py +518 -0
  167. operondbs-0.8.0/tests/unit/test_timetree_snapshots.py +938 -0
  168. operondbs-0.8.0/tests/unit/test_tools_edges.py +1077 -0
  169. operondbs-0.8.0/tests/unit/test_tools_execution_more.py +1250 -0
  170. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_tui.py +94 -19
  171. operondbs-0.8.0/tests/unit/test_tui_config.py +1621 -0
  172. operondbs-0.8.0/tests/unit/test_tui_flows_more.py +1126 -0
  173. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_tui_publish.py +19 -7
  174. operondbs-0.8.0/tests/unit/test_tui_screens_more.py +1663 -0
  175. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_tui_writes.py +44 -8
  176. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_views_release_reports_edges.py +4 -27
  177. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_workflow_cli.py +41 -0
  178. operondbs-0.7.2/.github/workflows/test.yml +0 -27
  179. operondbs-0.7.2/docs/en/contributor/development-testing.md +0 -48
  180. operondbs-0.7.2/docs/en/guides/external-analysis.md +0 -262
  181. operondbs-0.7.2/docs/en/guides/remote-storage.md +0 -113
  182. operondbs-0.7.2/docs/en/reference/behaviors-and-limitations.md +0 -192
  183. operondbs-0.7.2/docs/en/reference/cli-analysis.md +0 -111
  184. operondbs-0.7.2/docs/en/reference/recipe-fields.md +0 -328
  185. operondbs-0.7.2/docs/zh/contributor/development-testing.md +0 -57
  186. operondbs-0.7.2/docs/zh/guides/remote-storage.md +0 -130
  187. operondbs-0.7.2/docs/zh/reference/behaviors-and-limitations.md +0 -190
  188. operondbs-0.7.2/docs/zh/reference/cli-analysis.md +0 -152
  189. operondbs-0.7.2/docs/zh/reference/recipe-fields.md +0 -364
  190. operondbs-0.7.2/operon/environment.py +0 -92
  191. operondbs-0.7.2/tests/integration/test_analysis_tools.py +0 -492
  192. operondbs-0.7.2/tests/integration/test_python_packaging.py +0 -131
  193. operondbs-0.7.2/tests/unit/test_environment.py +0 -121
  194. operondbs-0.7.2/tests/unit/test_tools_edges.py +0 -361
  195. operondbs-0.7.2/tests/unit/test_tui_config.py +0 -683
  196. {operondbs-0.7.2 → operondbs-0.8.0}/.readthedocs.yaml +0 -0
  197. {operondbs-0.7.2 → operondbs-0.8.0}/LICENSE +0 -0
  198. {operondbs-0.7.2 → operondbs-0.8.0}/OperonDBS.egg-info/dependency_links.txt +0 -0
  199. {operondbs-0.7.2 → operondbs-0.8.0}/OperonDBS.egg-info/entry_points.txt +0 -0
  200. {operondbs-0.7.2 → operondbs-0.8.0}/OperonDBS.egg-info/top_level.txt +0 -0
  201. {operondbs-0.7.2 → operondbs-0.8.0}/README.md +0 -0
  202. {operondbs-0.7.2 → operondbs-0.8.0}/README_ZH.md +0 -0
  203. {operondbs-0.7.2 → operondbs-0.8.0}/benchmarks/qc_representative_entities.tsv +0 -0
  204. {operondbs-0.7.2 → operondbs-0.8.0}/docs/_static/language-switcher.js +0 -0
  205. {operondbs-0.7.2 → operondbs-0.8.0}/docs/_static/operon.css +0 -0
  206. {operondbs-0.7.2 → operondbs-0.8.0}/docs/_templates/layout.html +0 -0
  207. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/index.md +0 -0
  208. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/release-lifecycle.md +0 -0
  209. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/architecture/taxonomy-coverage.md +0 -0
  210. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/contributor/documentation-deployment.md +0 -0
  211. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/contributor/index.md +0 -0
  212. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/contributor/pypi-release.md +0 -0
  213. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/getting-started/daily-workflow.md +0 -0
  214. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/getting-started/index.md +0 -0
  215. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/getting-started/installation.md +0 -0
  216. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/ncbi-datasets.md +0 -0
  217. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/taxonomy-coverage.md +0 -0
  218. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/guides/troubleshooting.md +0 -0
  219. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/operations/index.md +0 -0
  220. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/operations/ncbi-recovery-migration.md +0 -0
  221. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/cli-tui.md +0 -0
  222. {operondbs-0.7.2 → operondbs-0.8.0}/docs/en/reference/index.md +0 -0
  223. {operondbs-0.7.2 → operondbs-0.8.0}/docs/index.md +0 -0
  224. {operondbs-0.7.2 → operondbs-0.8.0}/docs/requirements.txt +0 -0
  225. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/release-lifecycle.md +0 -0
  226. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/architecture/taxonomy-coverage.md +0 -0
  227. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/contributor/documentation-deployment.md +0 -0
  228. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/contributor/index.md +0 -0
  229. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/contributor/pypi-release.md +0 -0
  230. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/getting-started/daily-workflow.md +0 -0
  231. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/getting-started/index.md +0 -0
  232. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/getting-started/installation.md +0 -0
  233. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/guides/troubleshooting.md +0 -0
  234. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/operations/index.md +0 -0
  235. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/operations/ncbi-recovery-migration.md +0 -0
  236. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/cli-tui.md +0 -0
  237. {operondbs-0.7.2 → operondbs-0.8.0}/docs/zh/reference/index.md +0 -0
  238. {operondbs-0.7.2 → operondbs-0.8.0}/operon/__init__.py +0 -0
  239. {operondbs-0.7.2 → operondbs-0.8.0}/operon/__main__.py +0 -0
  240. {operondbs-0.7.2 → operondbs-0.8.0}/operon/adapters/__init__.py +0 -0
  241. {operondbs-0.7.2 → operondbs-0.8.0}/operon/backup.py +0 -0
  242. {operondbs-0.7.2 → operondbs-0.8.0}/operon/config.py +0 -0
  243. {operondbs-0.7.2 → operondbs-0.8.0}/operon/coverage.py +0 -0
  244. {operondbs-0.7.2 → operondbs-0.8.0}/operon/files.py +0 -0
  245. {operondbs-0.7.2 → operondbs-0.8.0}/operon/import_wizard.py +0 -0
  246. {operondbs-0.7.2 → operondbs-0.8.0}/operon/lifecycle.py +0 -0
  247. {operondbs-0.7.2 → operondbs-0.8.0}/operon/lineage.py +0 -0
  248. {operondbs-0.7.2 → operondbs-0.8.0}/operon/metadata_files.py +0 -0
  249. {operondbs-0.7.2 → operondbs-0.8.0}/operon/ncbi_reconcile.py +0 -0
  250. {operondbs-0.7.2 → operondbs-0.8.0}/operon/profiles.py +0 -0
  251. {operondbs-0.7.2/operon/qc_module → operondbs-0.8.0/operon/qc}/_parsers.pyx +0 -0
  252. {operondbs-0.7.2/operon/qc_module → operondbs-0.8.0/operon/qc}/parsers.py +0 -0
  253. {operondbs-0.7.2 → operondbs-0.8.0}/operon/release.py +0 -0
  254. {operondbs-0.7.2 → operondbs-0.8.0}/operon/reports.py +0 -0
  255. {operondbs-0.7.2 → operondbs-0.8.0}/operon/rules.py +0 -0
  256. {operondbs-0.7.2 → operondbs-0.8.0}/operon/schema.py +0 -0
  257. {operondbs-0.7.2 → operondbs-0.8.0}/operon/shutdown.py +0 -0
  258. {operondbs-0.7.2 → operondbs-0.8.0}/operon/table_import.py +0 -0
  259. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/__init__.py +0 -0
  260. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/app.py +0 -0
  261. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/app.tcss +0 -0
  262. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/assets/splash.png +0 -0
  263. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/assets/splash.rgb.z +0 -0
  264. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/__init__.py +0 -0
  265. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/coverage.py +0 -0
  266. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/decisions.py +0 -0
  267. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/entities.py +0 -0
  268. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/files.py +0 -0
  269. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/files_ops.py +0 -0
  270. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/screens/home.py +0 -0
  271. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/splash.py +0 -0
  272. {operondbs-0.7.2 → operondbs-0.8.0}/operon/tui/splash_terminal.py +0 -0
  273. {operondbs-0.7.2 → operondbs-0.8.0}/operon/utils.py +0 -0
  274. {operondbs-0.7.2 → operondbs-0.8.0}/setup.cfg +0 -0
  275. {operondbs-0.7.2 → operondbs-0.8.0}/tests/__init__.py +0 -0
  276. {operondbs-0.7.2 → operondbs-0.8.0}/tests/compatibility/__init__.py +0 -0
  277. {operondbs-0.7.2 → operondbs-0.8.0}/tests/helpers.py +0 -0
  278. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/__init__.py +0 -0
  279. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/test_analysis_resume.py +0 -0
  280. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/test_analysis_shutdown.py +0 -0
  281. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/test_execution_backends.py +0 -0
  282. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/test_lineage_cascade.py +0 -0
  283. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/test_ncbi_datasets_adapter.py +0 -0
  284. {operondbs-0.7.2 → operondbs-0.8.0}/tests/integration/test_pipeline_and_release.py +0 -0
  285. {operondbs-0.7.2 → operondbs-0.8.0}/tests/regression/__init__.py +0 -0
  286. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/__init__.py +0 -0
  287. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_docs_versions.py +0 -0
  288. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_export.py +0 -0
  289. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_import_wizard_edges.py +0 -0
  290. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_lineage.py +0 -0
  291. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_ncbi_edge_cases.py +0 -0
  292. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_ncbi_reconcile_edges.py +0 -0
  293. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_rules_schema_edges.py +0 -0
  294. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_schema_and_metadata.py +0 -0
  295. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_shutdown.py +0 -0
  296. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_table_import_edges.py +0 -0
  297. {operondbs-0.7.2 → operondbs-0.8.0}/tests/unit/test_tui_splash_terminal.py +0 -0
@@ -74,7 +74,8 @@ jobs:
74
74
  CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*"
75
75
  CIBW_SKIP: "*-musllinux_*"
76
76
  CIBW_TEST_COMMAND: >-
77
- python -c "import operon; import operon.qc_module._parsers;
77
+ python -c "import operon; import operon.qc._parsers;
78
+ import operon.qc._alignment;
78
79
  print(operon.__version__)" && operon --help
79
80
  - uses: actions/upload-artifact@v7
80
81
  with:
@@ -0,0 +1,51 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+
9
+ # A newer push supersedes an in-flight run of the same ref.
10
+ concurrency:
11
+ group: ${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ jobs:
15
+ # Documentation is cheap to build and independent of the test matrix, so it
16
+ # runs as its own job in parallel instead of gating the ten test jobs.
17
+ docs:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ - uses: actions/setup-python@v7
22
+ with:
23
+ python-version: "3.12"
24
+ cache: pip
25
+ cache-dependency-path: pyproject.toml
26
+ - run: python -m pip install --upgrade pip
27
+ - run: python -m pip install -e '.[docs]'
28
+ - run: sphinx-build -W --keep-going -b html docs docs/_build/html
29
+
30
+ pytest:
31
+ strategy:
32
+ fail-fast: false
33
+ matrix:
34
+ os: [ ubuntu-latest, macos-latest ]
35
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
36
+ runs-on: ${{ matrix.os }}
37
+ steps:
38
+ - uses: actions/checkout@v6
39
+ - uses: actions/setup-python@v7
40
+ with:
41
+ python-version: ${{ matrix.python-version }}
42
+ cache: pip
43
+ cache-dependency-path: pyproject.toml
44
+ - run: python -m pip install --upgrade pip
45
+ - run: python -m pip install -e '.[test]'
46
+ - run: python -m pytest -n 2 --dist loadfile
47
+ - if: ${{ !cancelled() }}
48
+ uses: codecov/codecov-action@v7
49
+ with:
50
+ token: ${{ secrets.CODECOV_TOKEN }}
51
+ report_type: test_results
@@ -5,7 +5,8 @@ dist/
5
5
  __pycache__/
6
6
  *.py[cod]
7
7
  *.so
8
- operon/qc_module/_parsers.c
8
+ operon/qc/_parsers.c
9
+ operon/qc/_alignment.c
9
10
  *.egg-info/
10
11
  *.sqlite
11
12
  *.sqlite-wal
@@ -14,6 +15,7 @@ logs/
14
15
  raw/
15
16
  standardized/
16
17
  qc/
18
+ !operon/qc/
17
19
  analysis/
18
20
  reports/
19
21
  releases/
@@ -26,3 +28,6 @@ junit.xml
26
28
  htmlcov/
27
29
  /site/
28
30
  /docs/_build/
31
+ /workflows/
32
+ /HPC/
33
+ .matrix/
@@ -26,8 +26,8 @@ principle-to-implementation mapping.
26
26
 
27
27
  Current version markers (must stay consistent across code and docs):
28
28
 
29
- - `operon` 0.7.0 (`pyproject.toml`)
30
- - database schema 2.9 (`operon/database.py`, `SCHEMA_VERSION`)
29
+ - `operon` 0.8.0 (`pyproject.toml`)
30
+ - database schema 2.11 (`operon/database.py`, `SCHEMA_VERSION`)
31
31
  - metadata schema 1.4 (`operon/schema.py`, `METADATA_SCHEMA_VERSION`)
32
32
 
33
33
  The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
@@ -36,17 +36,27 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
36
36
 
37
37
  - `operon/` — the Python package (CLI entry points: `operon/cli.py`,
38
38
  `operon/__main__.py`; console script `operon = operon.cli:main`).
39
- - `operon/adapters/` — external source adapters (currently NCBI Datasets,
40
- offline-first: JSON/JSONL, ZIP, or unpacked directories, plus optional
41
- online download).
42
- - `operon/qc_module/` — streaming FASTA/FASTQ/GFF3/protein parsers and
43
- built-in QC stages. `parsers.py` is the pure-Python reference
39
+ - `operon/adapters/` — external source adapters: NCBI Datasets
40
+ (offline-first: JSON/JSONL, ZIP, or unpacked directories, plus optional
41
+ online download) and TimeTree (query-cache-only REST client caching the
42
+ verbatim responses of exact queries under `adapters_cache/timetree/`;
43
+ mirroring or redistribution is forbidden by TimeTree's terms).
44
+ - `operon/qc/` — home for all QC functionality: streaming
45
+ FASTA/FASTQ/GFF3/protein parsers and built-in QC stages, plus alignment
46
+ QC (`alignment.py`, the pure-Python multiple-alignment QC reference
47
+ implementation behind `operon alignment-qc`, and `_alignment.pyx`, its
48
+ Cython production backend; parity enforced by
49
+ `tests/regression/test_cython_alignment_parity.py`). `parsers.py` is the
50
+ pure-Python reference
44
51
  implementation; `_parsers.pyx` is the Cython-accelerated build of the
45
- same API (compiled in place as `operon.qc_module._parsers`). The Cython
52
+ same API (compiled in place as `operon.qc._parsers`). The Cython
46
53
  module is the required production backend; the pure-Python module is the
47
54
  behavioral reference used by regression tests. Both must produce
48
55
  identical metrics and error messages (enforced by
49
- `tests/regression/test_cython_parser_parity.py`).
56
+ `tests/regression/test_cython_parser_parity.py`). `measure.py` backs
57
+ `operon qc-measure`, a project-independent measurement-only path whose
58
+ JSON payload can be imported back through `operon import-qc` (the remote
59
+ built-in QC workflow).
50
60
  - `operon/execution.py` — execution backends for external commands:
51
61
  `local` subprocess, `slurm` (sbatch submit + squeue poll), and `ssh`
52
62
  (Paramiko; HPC head nodes and cloud VMs, optionally through remote
@@ -96,9 +106,18 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
96
106
  NCBI Taxonomy snapshots and coverage denominators), `release.py` +
97
107
  `export.py` (immutable releases and selective exports), `lifecycle.py`
98
108
  (audited reversible entity retirement), `lineage.py` (adopting external
99
- workflow outputs), `backup.py`, `reports.py`, `table_import.py`,
109
+ workflow outputs), `sequence_tools.py` (alignment-driven domain
110
+ extraction and sequence selection), `classify.py` (versioned
111
+ sequence_classification profiles labelling `sequence_labels`),
112
+ `fanout.py` (data-derived fan-out of registered sequence files into
113
+ per-unit FASTAs under `analysis/derived/`, selected downstream by recipe
114
+ `file_role_prefix`), `timetree.py` (TimeTree query/calibration CLI
115
+ group backed by the query-cache adapter), `backup.py`, `reports.py`,
116
+ `table_import.py`,
100
117
  `import_wizard.py`, `entity_view.py`, `environment.py`
101
- (execution-environment capture), `shutdown.py` (graceful SIGINT/SIGTERM
118
+ (execution-environment capture with at-capture redaction; recipe
119
+ `environment_policy` governs environment-aware cache reuse),
120
+ `shutdown.py` (graceful SIGINT/SIGTERM
102
121
  handling), `ncbi_reconcile.py` (development-era adapter anomaly repair),
103
122
  `demo.py` (deterministic synthetic demo project), `errors.py`,
104
123
  `utils.py`.
@@ -112,6 +131,9 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
112
131
  (`.readthedocs.yaml`).
113
132
  - `benchmarks/` — representative entity sets for QC performance diagnostics
114
133
  (see `docs/*/operations/qc-performance.md`).
134
+ - `scripts/` — local developer tooling; `setup-test-matrix.sh` and
135
+ `run-test-matrix.sh` build and drive the uv-managed Python 3.10–3.14
136
+ matrix under .matrix/ (see `docs/*/contributor/development-testing.md`).
115
137
 
116
138
  ## Setup, test, and build
117
139
 
@@ -123,17 +145,22 @@ python3 -m venv .venv && source .venv/bin/activate
123
145
  python -m pip install -e '.[dev]' # runtime + pytest + Cython + Sphinx;
124
146
  # also compiles the qc parsers extension
125
147
 
126
- python -m pytest # full suite (coverage gate: 90% branch)
148
+ python -m pytest # full suite (coverage gate: >=95% combined
149
+ # line+branch, and >=95% branch coverage)
127
150
  python -m pytest tests/unit # by category: unit / integration /
128
151
  # regression / compatibility
129
152
 
153
+ python -m pytest --lf -q --no-cov # iterate: last failures, no coverage
154
+ python -m pytest -n 4 --dist loadfile # parallel full suite (xdist, in the dev/test extras)
155
+ scripts/run-test-matrix.sh # whole suite on 3.10-3.14 concurrently
156
+
130
157
  python setup.py build_ext --inplace # rebuild only the Cython extension
131
158
 
132
159
  sphinx-build -W --keep-going -b html docs docs/_build/html # strict docs build
133
160
  ```
134
161
 
135
162
  Run the relevant test category after any change; run the full suite before
136
- considering work done. CI (`.github/workflows/deploy.yml`) runs pytest on
163
+ considering work done. CI (`.github/workflows/test.yml`) runs pytest on
137
164
  Python 3.10–3.14 and the strict Sphinx build. Releases are published
138
165
  exclusively to PyPI; see `docs/*/contributor/pypi-release.md`.
139
166
 
@@ -170,6 +197,11 @@ asks.
170
197
  exists only for pre-1.0 databases and is scheduled for removal at the 1.0
171
198
  release; check it before touching `operon/database.py` migrations or the
172
199
  NCBI adapter's schema-upgrade path.
200
+ - The `operon.qc` module uses CFFI (specifically, Cython), so static analysis
201
+ tools, such as `flake8`, will report "undefined reference" errors (F821).
202
+ Using `# noqa: F821` behind the line **ONLY** when importing `operon.qc`
203
+ and an F821 error is likely to occur. Globally ignoring F821 is **STRICKLY
204
+ PROHIBITED**.
173
205
 
174
206
  ## Documentation sync
175
207
 
@@ -186,7 +218,7 @@ change:
186
218
  - Contributor-facing processes → `docs/*/contributor/`; navigation →
187
219
  `docs/*/index.md`
188
220
 
189
- Version markers in docs (`operon` 0.7.0, database schema 2.9, metadata
221
+ Version markers in docs (`operon` 0.8.0, database schema 2.11, metadata
190
222
  schema 1.4) must match `pyproject.toml` and the code. Do not write the
191
223
  current values literally in Markdown sources: use the `myst_substitutions`
192
224
  references `{{ operon_version }}`, `{{ db_schema }}`, and
@@ -14,4 +14,5 @@ prune docs/_build
14
14
  recursive-include operon *.py *.pyx *.tcss
15
15
  recursive-include operon/tui/assets *.png *.z *.md
16
16
  recursive-include tests *.py
17
- exclude operon/qc_module/_parsers.c
17
+ exclude operon/qc/_parsers.c
18
+ exclude operon/qc/_alignment.c
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OperonDBS
3
- Version: 0.7.2
3
+ Version: 0.8.0
4
4
  Summary: Operon the Database System: Archive, Quality-Control, Organize, Analyze and Release Your Bio-Data
5
5
  Author-email: hyli360 <lihuanyu2003@gmail.com>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -8,30 +8,26 @@ Project-URL: Repository, https://github.com/HYLi360/Operon
8
8
  Project-URL: Issues, https://github.com/HYLi360/Operon/issues
9
9
  Classifier: Operating System :: MacOS
10
10
  Classifier: Operating System :: POSIX :: Linux
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Programming Language :: Python :: 3.14
11
+ Classifier: Programming Language :: Python :: 3 :: Only
17
12
  Requires-Python: >=3.10
18
13
  Description-Content-Type: text/markdown
19
14
  License-File: LICENSE
20
15
  Requires-Dist: aiohttp>=3.9
21
16
  Requires-Dist: biopython>=1.85
22
- Requires-Dist: PyYAML>=6.0
23
17
  Requires-Dist: paramiko>=3.4
18
+ Requires-Dist: PyYAML>=6.0
24
19
  Requires-Dist: questionary>=2.1
25
20
  Requires-Dist: requests>=2.32
26
21
  Requires-Dist: textual>=8.0
27
22
  Provides-Extra: test
28
23
  Requires-Dist: coverage>=7.0; extra == "test"
24
+ Requires-Dist: cython>=3.0; extra == "test"
29
25
  Requires-Dist: paramiko>=3.4; extra == "test"
30
- Requires-Dist: pytest-cov>=7.0; extra == "test"
31
26
  Requires-Dist: pytest>=8.0; extra == "test"
27
+ Requires-Dist: pytest-cov>=7.0; extra == "test"
28
+ Requires-Dist: pytest-xdist>=3.8; extra == "test"
32
29
  Requires-Dist: setuptools>=77; extra == "test"
33
30
  Requires-Dist: textual>=8.0; extra == "test"
34
- Requires-Dist: cython>=3.0; extra == "test"
35
31
  Provides-Extra: docs
36
32
  Requires-Dist: Sphinx<9,>=7.4; extra == "docs"
37
33
  Requires-Dist: myst-parser<5,>=4.0; extra == "docs"
@@ -40,8 +36,10 @@ Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "docs"
40
36
  Provides-Extra: dev
41
37
  Requires-Dist: coverage>=7.0; extra == "dev"
42
38
  Requires-Dist: cython>=3.0; extra == "dev"
43
- Requires-Dist: pytest-cov>=7.0; extra == "dev"
44
39
  Requires-Dist: pytest>=8.0; extra == "dev"
40
+ Requires-Dist: pytest-cov>=7.0; extra == "dev"
41
+ Requires-Dist: pytest-xdist>=3.8; extra == "dev"
42
+ Requires-Dist: setuptools>=77; extra == "dev"
45
43
  Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "dev"
46
44
  Requires-Dist: Sphinx<9,>=7.4; extra == "dev"
47
45
  Requires-Dist: myst-parser<5,>=4.0; extra == "dev"
@@ -50,8 +50,10 @@ docs/en/guides/file-archiving.md
50
50
  docs/en/guides/index.md
51
51
  docs/en/guides/metadata-import.md
52
52
  docs/en/guides/ncbi-datasets.md
53
+ docs/en/guides/phylogenetics-workflow.md
53
54
  docs/en/guides/qc-profiles.md
54
55
  docs/en/guides/remote-execution.md
56
+ docs/en/guides/remote-first.md
55
57
  docs/en/guides/remote-storage.md
56
58
  docs/en/guides/taxonomy-coverage.md
57
59
  docs/en/guides/troubleshooting.md
@@ -101,8 +103,10 @@ docs/zh/guides/file-archiving.md
101
103
  docs/zh/guides/index.md
102
104
  docs/zh/guides/metadata-import.md
103
105
  docs/zh/guides/ncbi-datasets.md
106
+ docs/zh/guides/phylogenetics-workflow.md
104
107
  docs/zh/guides/qc-profiles.md
105
108
  docs/zh/guides/remote-execution.md
109
+ docs/zh/guides/remote-first.md
106
110
  docs/zh/guides/remote-storage.md
107
111
  docs/zh/guides/taxonomy-coverage.md
108
112
  docs/zh/guides/troubleshooting.md
@@ -127,6 +131,7 @@ docs/zh/reference/recipe-parsers-examples.md
127
131
  operon/__init__.py
128
132
  operon/__main__.py
129
133
  operon/backup.py
134
+ operon/classify.py
130
135
  operon/cli.py
131
136
  operon/config.py
132
137
  operon/coverage.py
@@ -134,9 +139,11 @@ operon/database.py
134
139
  operon/demo.py
135
140
  operon/entity_view.py
136
141
  operon/environment.py
142
+ operon/environment_capture.py
137
143
  operon/errors.py
138
144
  operon/execution.py
139
145
  operon/export.py
146
+ operon/fanout.py
140
147
  operon/files.py
141
148
  operon/import_wizard.py
142
149
  operon/lifecycle.py
@@ -149,17 +156,23 @@ operon/remotes.py
149
156
  operon/reports.py
150
157
  operon/rules.py
151
158
  operon/schema.py
159
+ operon/sequence_tools.py
152
160
  operon/shutdown.py
153
161
  operon/table_import.py
154
162
  operon/taxonomy.py
163
+ operon/timetree.py
155
164
  operon/tools.py
156
165
  operon/utils.py
157
166
  operon/workflow.py
158
167
  operon/adapters/__init__.py
159
168
  operon/adapters/ncbi_datasets.py
160
- operon/qc_module/__init__.py
161
- operon/qc_module/_parsers.pyx
162
- operon/qc_module/parsers.py
169
+ operon/adapters/timetree.py
170
+ operon/qc/__init__.py
171
+ operon/qc/_alignment.pyx
172
+ operon/qc/_parsers.pyx
173
+ operon/qc/alignment.py
174
+ operon/qc/measure.py
175
+ operon/qc/parsers.py
163
176
  operon/tui/__init__.py
164
177
  operon/tui/actions.py
165
178
  operon/tui/app.py
@@ -189,50 +202,75 @@ tests/integration/__init__.py
189
202
  tests/integration/test_analysis_resume.py
190
203
  tests/integration/test_analysis_shutdown.py
191
204
  tests/integration/test_analysis_tools.py
205
+ tests/integration/test_classify_sequences.py
192
206
  tests/integration/test_execution_backends.py
207
+ tests/integration/test_fanout_analyze.py
193
208
  tests/integration/test_lineage_cascade.py
194
209
  tests/integration/test_ncbi_datasets_adapter.py
195
210
  tests/integration/test_pipeline_and_release.py
196
211
  tests/integration/test_python_packaging.py
212
+ tests/integration/test_qc_remote_measure.py
197
213
  tests/integration/test_taxonomy_coverage.py
198
214
  tests/regression/__init__.py
199
215
  tests/regression/test_correctness.py
216
+ tests/regression/test_cython_alignment_parity.py
200
217
  tests/regression/test_cython_parser_parity.py
201
218
  tests/regression/test_parser_semantics.py
202
219
  tests/unit/__init__.py
220
+ tests/unit/test_alignment_qc.py
221
+ tests/unit/test_classify.py
203
222
  tests/unit/test_cli_edges.py
204
223
  tests/unit/test_config_workflow_edges.py
224
+ tests/unit/test_core_coverage_more.py
205
225
  tests/unit/test_coverage_edges.py
206
226
  tests/unit/test_database_edges_more.py
207
227
  tests/unit/test_docs_versions.py
208
228
  tests/unit/test_environment.py
229
+ tests/unit/test_environment_capture.py
209
230
  tests/unit/test_execution.py
210
231
  tests/unit/test_execution_edges.py
211
232
  tests/unit/test_export.py
233
+ tests/unit/test_fanout.py
212
234
  tests/unit/test_files_edges.py
235
+ tests/unit/test_follow.py
213
236
  tests/unit/test_import_backup_show.py
214
237
  tests/unit/test_import_wizard_edges.py
215
238
  tests/unit/test_lifecycle.py
216
239
  tests/unit/test_lineage.py
240
+ tests/unit/test_misc_coverage_more.py
241
+ tests/unit/test_ncbi_coverage_gaps.py
217
242
  tests/unit/test_ncbi_edge_cases.py
218
243
  tests/unit/test_ncbi_reconcile_edges.py
219
244
  tests/unit/test_parser_edge_paths.py
220
245
  tests/unit/test_qc_and_rules.py
221
246
  tests/unit/test_qc_edges_more.py
247
+ tests/unit/test_qc_measure.py
248
+ tests/unit/test_qc_remote_only.py
222
249
  tests/unit/test_recipe_history.py
250
+ tests/unit/test_remote_taxonomy_more.py
251
+ tests/unit/test_remote_transport_edges.py
223
252
  tests/unit/test_remotes.py
224
253
  tests/unit/test_remotes_edges.py
225
254
  tests/unit/test_rules_schema_edges.py
255
+ tests/unit/test_schema_2_10.py
256
+ tests/unit/test_schema_2_11.py
226
257
  tests/unit/test_schema_2_9.py
227
258
  tests/unit/test_schema_and_metadata.py
259
+ tests/unit/test_sequence_tools.py
260
+ tests/unit/test_sequences.py
228
261
  tests/unit/test_shutdown.py
229
262
  tests/unit/test_support_edges.py
230
263
  tests/unit/test_table_import_edges.py
231
264
  tests/unit/test_taxonomy_edges.py
265
+ tests/unit/test_timetree.py
266
+ tests/unit/test_timetree_snapshots.py
232
267
  tests/unit/test_tools_edges.py
268
+ tests/unit/test_tools_execution_more.py
233
269
  tests/unit/test_tui.py
234
270
  tests/unit/test_tui_config.py
271
+ tests/unit/test_tui_flows_more.py
235
272
  tests/unit/test_tui_publish.py
273
+ tests/unit/test_tui_screens_more.py
236
274
  tests/unit/test_tui_splash_terminal.py
237
275
  tests/unit/test_tui_writes.py
238
276
  tests/unit/test_views_release_reports_edges.py
@@ -1,7 +1,7 @@
1
1
  aiohttp>=3.9
2
2
  biopython>=1.85
3
- PyYAML>=6.0
4
3
  paramiko>=3.4
4
+ PyYAML>=6.0
5
5
  questionary>=2.1
6
6
  requests>=2.32
7
7
  textual>=8.0
@@ -9,8 +9,10 @@ textual>=8.0
9
9
  [dev]
10
10
  coverage>=7.0
11
11
  cython>=3.0
12
- pytest-cov>=7.0
13
12
  pytest>=8.0
13
+ pytest-cov>=7.0
14
+ pytest-xdist>=3.8
15
+ setuptools>=77
14
16
  Sphinx<9,>=7.4
15
17
  myst-parser<5,>=4.0
16
18
  sphinx-rtd-theme<4,>=3.0
@@ -28,9 +30,10 @@ tomli>=2.0
28
30
 
29
31
  [test]
30
32
  coverage>=7.0
33
+ cython>=3.0
31
34
  paramiko>=3.4
32
- pytest-cov>=7.0
33
35
  pytest>=8.0
36
+ pytest-cov>=7.0
37
+ pytest-xdist>=3.8
34
38
  setuptools>=77
35
39
  textual>=8.0
36
- cython>=3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OperonDBS
3
- Version: 0.7.2
3
+ Version: 0.8.0
4
4
  Summary: Operon the Database System: Archive, Quality-Control, Organize, Analyze and Release Your Bio-Data
5
5
  Author-email: hyli360 <lihuanyu2003@gmail.com>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -8,30 +8,26 @@ Project-URL: Repository, https://github.com/HYLi360/Operon
8
8
  Project-URL: Issues, https://github.com/HYLi360/Operon/issues
9
9
  Classifier: Operating System :: MacOS
10
10
  Classifier: Operating System :: POSIX :: Linux
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Programming Language :: Python :: 3.14
11
+ Classifier: Programming Language :: Python :: 3 :: Only
17
12
  Requires-Python: >=3.10
18
13
  Description-Content-Type: text/markdown
19
14
  License-File: LICENSE
20
15
  Requires-Dist: aiohttp>=3.9
21
16
  Requires-Dist: biopython>=1.85
22
- Requires-Dist: PyYAML>=6.0
23
17
  Requires-Dist: paramiko>=3.4
18
+ Requires-Dist: PyYAML>=6.0
24
19
  Requires-Dist: questionary>=2.1
25
20
  Requires-Dist: requests>=2.32
26
21
  Requires-Dist: textual>=8.0
27
22
  Provides-Extra: test
28
23
  Requires-Dist: coverage>=7.0; extra == "test"
24
+ Requires-Dist: cython>=3.0; extra == "test"
29
25
  Requires-Dist: paramiko>=3.4; extra == "test"
30
- Requires-Dist: pytest-cov>=7.0; extra == "test"
31
26
  Requires-Dist: pytest>=8.0; extra == "test"
27
+ Requires-Dist: pytest-cov>=7.0; extra == "test"
28
+ Requires-Dist: pytest-xdist>=3.8; extra == "test"
32
29
  Requires-Dist: setuptools>=77; extra == "test"
33
30
  Requires-Dist: textual>=8.0; extra == "test"
34
- Requires-Dist: cython>=3.0; extra == "test"
35
31
  Provides-Extra: docs
36
32
  Requires-Dist: Sphinx<9,>=7.4; extra == "docs"
37
33
  Requires-Dist: myst-parser<5,>=4.0; extra == "docs"
@@ -40,8 +36,10 @@ Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "docs"
40
36
  Provides-Extra: dev
41
37
  Requires-Dist: coverage>=7.0; extra == "dev"
42
38
  Requires-Dist: cython>=3.0; extra == "dev"
43
- Requires-Dist: pytest-cov>=7.0; extra == "dev"
44
39
  Requires-Dist: pytest>=8.0; extra == "dev"
40
+ Requires-Dist: pytest-cov>=7.0; extra == "dev"
41
+ Requires-Dist: pytest-xdist>=3.8; extra == "dev"
42
+ Requires-Dist: setuptools>=77; extra == "dev"
45
43
  Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "dev"
46
44
  Requires-Dist: Sphinx<9,>=7.4; extra == "dev"
47
45
  Requires-Dist: myst-parser<5,>=4.0; extra == "dev"
@@ -17,7 +17,7 @@ REPO_ROOT = DOCS_DIR.parent
17
17
 
18
18
  project = "Operon"
19
19
  author = "Project Operon Development Group"
20
- copyright = f"2026, {author}. All Rights Reserved."
20
+ copyright = f"2026, {author}. All Rights Reserved"
21
21
 
22
22
 
23
23
  def _package_version() -> str:
@@ -4,10 +4,10 @@
4
4
 
5
5
  The built-in source adapter currently covers NCBI Datasets; sources such as ENA remain part of the future extension boundary. Taxonomy coverage currently supports only NCBI Taxonomy; GTDB and the NCBI↔GTDB crosswalk are not yet implemented. Built-in QC covers file level, generic sequence basics for non-genome FASTA (CDS/protein), reads basics, assembly structure, and annotation structure. BUSCO is natively integrated through directory output and a JSON summary parser; tools without a parser yet — QUAST, Merqury, Kraken2, CheckM2, and similar — can still be integrated through `run-external` + `import-qc`. Downstream comparative-genomics analysis is done by external workflows in `analysis/`; `operon` is responsible for data admission, provenance, and publication.
6
6
 
7
- The contract between downstream workflows and the database is a closed loop formed by `operon export` and `operon adopt`: export materializes the selected entities by file identity into a `data/<entity_type>/<entity_id>/<filename>` layout, accompanied by `manifest.tsv` (with SHA-256 recomputed over the materialized bytes), a `qc.tsv` QC long-table snapshot, `checksums.sha256`, and `provenance.json` (the input-side manifest); after consuming this artifact set, the external workflow uses adopt to re-register derived artifacts as first-class manifest members — materialized under `analysis/adopted/<entity_id>/`, inheriting the ingest idempotency/conflict invariants, with file-to-file lineage edges recorded in `file_lineage` (the output-side manifest). Adopted products can be QC'd, evaluated, exported, released, and selected by `analyze` as inputs of downstream recipes (cascading analysis). Downstream workflows should read and write through this contract instead of reading the database directly. Orchestration of cascading workflows (dependency graphs, parallelism, retries) belongs to workflow managers such as snakemake/nextflow; `operon` is responsible for data admission, lineage, and publication, while `run-pipeline` only covers simple single-file chaining. The batch adopt manifest format is described in the [external analysis guide](../guides/external-analysis.md). Export is semantically complementary to release: release targets publication (QC-gated, immutable snapshot), while export targets analysis inputs (arbitrary selection criteria, materialized on demand).
7
+ The contract between downstream workflows and the database is a closed loop formed by `operon export` and `operon adopt`: export materializes the selected entities by file identity into a `data/<entity_type>/<entity_id>/<filename>` layout, accompanied by `manifest.tsv` (with SHA-256 recomputed over the materialized bytes), a `qc.tsv` QC long-table snapshot, `checksums.sha256`, and `provenance.json` (the input-side manifest); after consuming this artifact set, the external workflow uses adopt to re-register derived artifacts as first-class manifest members — materialized under `analysis/adopted/<entity_id>/`, inheriting the ingest idempotency/conflict invariants, with file-to-file lineage edges recorded in `file_lineage` (the output-side manifest). Adopted products can be QC'd, evaluated, exported, released, and selected by `analyze` as inputs of downstream recipes (cascading analysis). Downstream workflows should read and write through this contract instead of reading the database directly. Orchestration of cascading workflows (dependency graphs, parallelism, retries) belongs to workflow managers such as snakemake/nextflow; `operon` is responsible for data admission, lineage, and publication, while `run-pipeline` only covers simple single-file chaining. One carve-out on the admission side: when the number of downstream analysis units is data-dependent (for example subfamilies whose count is known only after classification), admitting those units is `operon`'s responsibility — `operon fanout` materializes and registers the per-unit files with lineage under `analysis/derived/`; orchestrating the runs over them still belongs to the executor and the workflow engine. The batch adopt manifest format is described in the [external analysis guide](../guides/external-analysis.md). Export is semantically complementary to release: release targets publication (QC-gated, immutable snapshot), while export targets analysis inputs (arbitrary selection criteria, materialized on demand).
8
8
 
9
9
  Deduplication is implemented by layer: byte-level duplication is already guaranteed by SHA-256 idempotence (same entity and role with identical bytes returns the same `FIL_`; different bytes are explicitly rejected); sequence-level duplication (normalized sequence digests / refget-style summaries) and biological near-duplication (Mash/ANI/k-mer similarity, duplicate clusters, and representative selection) are extension directions — they can be performed with external tools in `analysis/` with results written back to `qc_results`, and the representative-selection rules themselves should also be versioned.
10
10
 
11
11
  For scale, SQLite WAL plus indexes suits metadata on the order of millions of rows, and all sequence parsing is streaming; if inode sharing is explicitly acceptable, hard links can be used for `standardized/` or releases.
12
12
 
13
- Execution backends extend along the abstraction in `execution.py`: `local`, `slurm`, and `ssh` are provided today, and a new backend only needs to implement the same executor interface to plug into `run-external`/`analyze`. Cloud-vendor SDKs (AWS Batch, GCP Batch, etc.) and Slurm array jobs are not yet supported; remote storage currently consists of SFTP mirrors only, and object storage (S3 and the like) is likewise an extension direction.
13
+ Execution backends extend along the abstraction in `execution.py`: `local`, `slurm`, and `ssh` are provided today, and a new backend only needs to implement the same executor interface to plug into `run-external`/`analyze`. Cloud-vendor SDKs (AWS Batch, GCP Batch, etc.) and Slurm array jobs are not yet supported; remote storage currently consists of SFTP mirrors only, and object storage (S3 and the like) is likewise an extension direction. Data-dependent fan-out does not require array jobs: `operon fanout` admits each unit as its own manifest file, and `analyze` with a `file_role_prefix` recipe then submits one job per unit through any backend.
@@ -10,10 +10,12 @@ External programs such as BLAST/HMMER/BUSCO no longer require hand-assembled com
10
10
  4. Computes the reference database identity (single-file SHA-256 / directory fingerprint / explicit checksum);
11
11
  5. Looks up a completed cache by `analysis_name + file_id + input SHA + parameter fingerprint + tool version + database identity`, skipping on a hit; on a miss, enters the second-level continuation: if an old `completed` job for the same `(analysis, file_id)` has a matching input content hash and its recorded output artifact passes byte-level verification, that output is adopted (a new `completed` row is inserted under the current fingerprint, traced in the `changes` audit table, with status `adopted`); only otherwise is the job recomputed;
12
12
  6. On a miss, launches the program via `conda run`, a container prefix, or a direct path; both file and directory outputs must exist and be non-empty, with stdout/stderr written to disk;
13
- 7. Computes the file or directory content hash, parses top hits or the BUSCO JSON summary into `analysis_hits`/`analysis_results`, and syncs the same-named metrics into `qc_results`.
13
+ 7. Computes the file or directory content hash, parses top hits or the BUSCO JSON summary into `analysis_hits`/`analysis_results`, and syncs the same-named metrics into `qc_results`. Parsers with coordinates (`blast_tabular`, `hmmer_domtblout`, `rpsbproc_tabular`) additionally write every parsed hit to `analysis_alignments` as a structured row (query/subject IDs, hit rank, query/subject intervals, e-value, bitscore, percent identity, unmapped columns in `extra_json`), untruncated by `max_hits_per_query`; `report analysis --hits` reads this table.
14
14
 
15
15
  Directories use a deterministic tree hash composed of relative paths, empty directories, file sizes/contents, and symlink targets. `database_mode: mutable_cache` serves shared caches such as BUSCO's that gradually download lineages, identified by an explicit `database_version` as the logical version; immutable reference databases keep the default `reference` content identity.
16
16
 
17
+ A recipe may replace the single command with a `commands` chain (mutually exclusive with `arguments`). All rendered steps execute in order through the same executor and parent-tool `run_method` (one recipe, one environment — a deliberate capability limitation), under one run record and one `analysis_jobs` row. The first command is the recipe's logical owner: the job's `tool_version` and the version component of cache identity come from it — from the first block's own `version_args`/`version_pattern` when declared, otherwise from the tool-level probe (recipe loading forces the two to target the same program). A step invoking the parent `executable` inherits its already-probed version; another program may declare its own `version_args` and `version_pattern`, which are run through the same launcher/executor. Unconfigured secondary programs are explicitly recorded as version `unknown`; version flags are never guessed. Each step receives its own `logs/<run_id>.step<N>.stdout.log` / `.stderr.log`, and its `argv`, `executable`, `tool_version`, `tool_version_raw`, `version_source`, `version_command`, and `exit_code` are recorded in `execution_details.steps`. The first non-zero step aborts the chain and fails the job with a `step N/M failed` error. Intermediate artifacts live in a deterministic `${work_dir}` scratch directory (`<output_name>.work` next to the output), which is deleted and rebuilt before execution and removed after success or failure (`--keep-partial` keeps it). Expected-output validation, content hashing, and result parsing run once, after the last step. On SSH backends with a remote root the work directory is mapped to the remote root and created there like any other project path. The canonical example couples `rpsblast -outfmt 11` (ASN.1 archive) with independently versioned `rpsbproc`, whose tabular report is parsed by `rpsbproc_tabular`: domain rows from the `DATA`/`SESSION`/`QUERY`/`DOMAINS` blocks become full `analysis_alignments` rows (the query is the QUERY definition line, the subject is the accession, coordinates come from the `from`/`to` columns, and hit type/PSSM IDs/short name stay in `extra_json`), with EAV hits truncated to `max_hits_per_query` as usual. Every probed step version is mixed into the cache fingerprint: upgrading any step's program invalidates the exact cache even when the recipe text and the primary tool version are unchanged, with verified-output adoption applying as usual.
18
+
17
19
  Actual execution of external commands is taken over by the backend abstraction in `execution.py`; `run_external_command` selects a backend through `get_executor(project, backend)`:
18
20
 
19
21
  - `local` (default): the original local subprocess behavior, completely unchanged;
@@ -22,13 +24,17 @@ Actual execution of external commands is taken over by the backend abstraction i
22
24
 
23
25
  All three backends share the same provenance and correctness contract: exit codes, start/end times, and logs are written to `workflow_runs` and `logs/workflow.jsonl` as usual; SQLite additionally stores the executor, scheduler job ID, and resource/script details; success determination and input/output SHA-256 verification are unchanged; tool version probing on non-`local` backends also goes through the same backend. An individual recipe can override same-named fields of `execution.slurm` with a `slurm:` mapping (e.g. adjusting memory/time for BUSCO alone).
24
26
 
25
- Execution environment capture (schema 2.8, `environment.py`): all three backends probe the execution environment on every run. The normalized JSON document is stored in the `execution_environments` table; its primary key `environment_id` is the SHA-256 of that normalized JSON (content-addressed, so identical documents deduplicate automatically), and both `workflow_runs` and `analysis_jobs` reference it through their own `environment_id` column.
27
+ Execution environment capture (`environment.py`, `environment_capture.py`) stores normalized JSON in `execution_environments`, addressed by `environment_id`. Runs and analysis jobs reference that document; each command-chain step also records its own environment ID. No database migration is needed for the extended JSON document (`capture_schema: 1`). The document is redacted at capture time, before storage: the hostname is replaced by `sha256:` plus the first 16 hex characters of its SHA-256, and the `$HOME` prefix of path-like values (`PATH`, `CONDA_PREFIX`, `VIRTUAL_ENV`, the conda prefix) is replaced by `~`. Remote probes transfer the raw values (probe files are written with umask 077) and the controlling Python process redacts them before storage; a transient `home` probe key feeds the replacement and is then discarded, never entering the stored document or any fingerprint. The `environment_id` and sub-fingerprints are computed from the redacted document, so identical configurations captured on different hosts deduplicate to one record. Documents recorded before redaction was introduced are content-addressed and immutable and are not migrated; new captures produce new environment IDs.
28
+
29
+ The local, Slurm and SSH backends probe immediately before the payload. Slurm probes after `setup_commands` on the compute node; direct SSH probes in the payload shell's working directory. Recognized `conda`, `mamba` and `micromamba run` launchers run the probe through the same launcher and target name/prefix. Reading `conda-meta/*.json` requires no Python or package-manager CLI inside the target environment. Direct programs inherit the executor environment. Known opaque shell/container launchers and unsupported manager options are labelled `unsupported_launcher`, with `capture_scope: executor_only`; their outer environment is not claimed as the tool's Conda environment. Arbitrary custom wrappers are not introspected.
30
+
31
+ Conda snapshots retain package name, version, build, subdir, dependencies, archive URL and available SHA-256/MD5. Complete inventories include an `@EXPLICIT` reconstruction specification (SHA-256 preferred, MD5 fallback). Package fingerprints exclude installation prefixes and hostnames. Credential-bearing URL components (userinfo, `/t/` tokens and queries) are removed. Private channels may consequently require credentials supplied separately during restoration. Pip-installed distribution names detected through `INSTALLER` metadata are reported separately; neither pip packages, editable installs, manually modified files nor custom activation scripts are restored by the Conda specification. The package inventory describes original archives, not a verification of installed bytes. Empty, malformed, interrupted or unresolvable inventories cannot be exported as complete specifications.
32
+
33
+ Separate `system_fingerprint` and `hardware_fingerprint` cover OS/kernel/architecture, distribution and glibc, and CPU model/features, total memory and available NVIDIA GPU model/driver/compute capability respectively. Hostname, PATH, prefix, CPU affinity and runtime settings do not enter those two fingerprints. Selected locale, timezone and threading/device variables and CPU affinity are retained as context. These are Linux-oriented probes: missing CPU/GPU data is explicitly marked unavailable (GPU absence and probe failure are not distinguished); no dynamic metrics, GPU UUIDs or serial numbers are collected. Hardware fingerprints describe observed fields and do not certify hardware equivalence when fields are unavailable.
26
34
 
27
- - `local`: collects hostname, OS/kernel/architecture, Python and `operon` versions, the environment variables `PATH`, `CONDA_PREFIX`, `CONDA_DEFAULT_ENV`, `VIRTUAL_ENV`, `SINGULARITY_NAME`, `APPTAINER_NAME`, `container`, and a docker probe, all directly;
28
- - `slurm`: embeds the probe in the sbatch script, writes the result to `<run_id>.env` inside the job, and reads it back — the captured environment is the compute node's, not the submission host's;
29
- - `ssh`: direct mode runs the probe on the remote host through paramiko; remote Slurm mode embeds and retrieves the same job-side probe so it also records the compute node rather than the SSH login node.
35
+ Probes are best-effort, bounded by 30 seconds (NVIDIA query: 5 seconds), and require a POSIX shell, `base64` and common system utilities. GNU `timeout` bounds the probe when the host provides it; elsewhere — macOS ships no `timeout` — a POSIX watcher enforces the same 30-second bound, so a host without it still captures instead of reporting `unavailable`. Linux-only data sources such as `/proc` simply leave their fields unavailable. `capture_status` distinguishes complete, partial, failed, unavailable and unsupported launcher captures. Earlier documents remain readable and may lack these fields. Controller-only legacy probes remain available as fallback; their Python version must not be interpreted as the target interpreter version.
30
36
 
31
- A failed probe leaves the run's `environment_id` NULL without raising an error or affecting the run; rows from before 2.8 are likewise NULL.
37
+ Environment fingerprints also drive a recipe-level reuse policy: the optional recipe field `environment_policy` takes `ignore`, `warn` (the default) or `strict`, and any other value is rejected at configuration validation. The field enters the parameter fingerprint only when it is explicitly set — the same mechanism as `hmmer_mode` and the alignment column-mapping keys — so setting or changing it invalidates the completed cache exactly once. On a completed-cache hit with a policy other than `ignore`, `operon` compares an environment-relevance fingerprint: the composite of the document's `system_fingerprint`, `hardware_fingerprint` and `conda.package_fingerprint`. Hostname, paths, CPU affinity and runtime threading variables are deliberately excluded — the thread count already participates in the parameter fingerprint, and affinity is transient scheduler state. If the fingerprints match, the cache is reused as usual; on a mismatch, `warn` reuses the cached result while recording a warning in the run details and printing it, and `strict` treats the hit as a miss and recomputes. Backends without a pre-job probe (`slurm` and remote Slurm) cannot compare before reuse, so `strict` degrades to `warn` and the downgrade is recorded in the run details. When either side's environment document lacks sub-fingerprints (captures recorded before database schema {{ db_schema }}), the comparison is recorded as `unavailable`: `warn` reuses as usual and `strict` likewise degrades to `warn`, so legacy provenance never causes spurious recomputation.
32
38
 
33
39
  Recipe versioning and snapshots (schema 2.9): a recipe gains an optional `version:` field (a positive integer, default 1; invalid values are rejected at configuration validation). As `analyze` processes each candidate file it records the current recipe together with the verbatim spec of its referenced tool into the `recipe_snapshots` table: the snapshot document is `{"recipe": <the recipe's raw mapping>, "tool": <the referenced tool spec's raw mapping>}`, content-addressed by the SHA-256 of its canonicalized JSON and deduplicated by `UNIQUE(recipe_name, recipe_version, recipe_sha256)` — so edits to the tool definition also produce a new snapshot, and cache hits record a snapshot of the current configuration as well. `analysis_jobs.recipe_snapshot_id` points back to the exact configuration that produced the job; jobs adopted during resume inherit the original job's snapshot id (they were produced by that configuration, not today's). Inspect them with `operon recipes list / history / show`; the QC-profile counterparts recorded in `qc_profiles` are inspected with `operon profiles history / show`. Restoration via the CLI is print-only in both cases: a human copies the output back into the configuration YAML, and the CLI never rewrites files in place. The audited alternative is the TUI Config screen, whose structured editors save every change as the next version with a new snapshot and can restore any recorded snapshot into the editor (saving it creates the next version); TUI recipe saves normalize `tools.yaml` formatting and drop hand-written comments. <!-- version-pin -->
34
40
 
@@ -18,12 +18,12 @@ Guarantees of `ingest`:
18
18
  The `remotes:` section of `project.yaml` can configure one or more SFTP remote mirrors (`operon/remotes.py`), synchronizing manifest files to a remote without breaking the invariants of this section:
19
19
 
20
20
  - Plain files and directory artifacts are all verified by `sha256 + size_bytes`; when the server lacks `sha256sum`, SHA-256 is computed by streaming over SFTP, never degrading to size-only comparison; directories use exactly the same deterministic tree hash as locally (including empty directories and symlink targets);
21
- - The remote maintains an `operon-manifest.json` v2 manifest (project_id + relative_path → file_id/sha256/size/kind/synced_at). Manifest updates require the server to support the OpenSSH POSIX rename extension and are published via "unique temporary file + atomic replacement"; one push batch publishes the manifest exactly once, and read-modify-write is serialized through the remote atomic directory `.operon-manifest.lock`, so concurrent pushes from multiple control ends cannot lose entries;
21
+ - The remote maintains an `operon-manifest.json` v2 manifest (project_id + relative_path → file_id/sha256/size/kind/synced_at) whose updates are published atomically and serialized between concurrent control ends;
22
22
  - All relative paths are root-constrained on both sides; absolute paths, `..`, and path escapes are rejected. The remote manifest's `project_id` and every entry identity must match the local SQLite;
23
23
  - Every transfer reuses `workflow_runs` for provenance (step `push:<name>` / `pull:<name>`); successful locations are also cached in `file_locations`;
24
- - push/pull/evict use per-item result semantics: a failed item records `error` and the rest of the batch continues; the CLI returns non-zero if any error exists;
25
- - After `pull` restores a locally missing file, `files.status` returns to `CHECKSUM_VERIFIED`, and this change is written to `changes` just like the status changes from `verify`/`evict`;
26
- - `ingest --source` also directly accepts `sftp://[user@]host[:port]/path` and `remote://<name>/<path>`; the latter must exist in the remote manifest and is identity-checked first, while the former is downloaded and assigned a fresh identity by ingest, then follows exactly the same archiving flow as a local file.
24
+ - After `pull` restores a locally missing file, `files.status` returns to `CHECKSUM_VERIFIED`, and this change is written to `changes` just like the status changes from `verify`/`evict`.
25
+
26
+ Manifest publication and locking, batch semantics and per-item exit codes, and `sftp://`/`remote://` ingest are command-level details; see [Remote storage commands](../reference/cli-remote.md) and [File and QC Commands](../reference/cli-files-qc.md#ingest).
27
27
 
28
28
  Paramiko is a core runtime dependency but remains lazily imported, so local-only commands do not initialize the SSH stack.
29
29