OperonDBS 0.8.0__tar.gz → 0.8.2__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 (288) hide show
  1. {operondbs-0.8.0 → operondbs-0.8.2}/.github/workflows/publish.yml +1 -1
  2. {operondbs-0.8.0 → operondbs-0.8.2}/.github/workflows/test.yml +9 -8
  3. {operondbs-0.8.0 → operondbs-0.8.2}/.gitignore +1 -0
  4. {operondbs-0.8.0 → operondbs-0.8.2}/AGENTS.md +52 -10
  5. {operondbs-0.8.0 → operondbs-0.8.2}/OperonDBS.egg-info/PKG-INFO +8 -3
  6. {operondbs-0.8.0 → operondbs-0.8.2}/OperonDBS.egg-info/SOURCES.txt +9 -0
  7. {operondbs-0.8.0 → operondbs-0.8.2}/PKG-INFO +8 -3
  8. {operondbs-0.8.0 → operondbs-0.8.2}/README.md +8 -3
  9. {operondbs-0.8.0 → operondbs-0.8.2}/README_ZH.md +8 -3
  10. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/external-analysis.md +13 -3
  11. operondbs-0.8.2/docs/en/contributor/defect-tracking.md +71 -0
  12. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/contributor/development-testing.md +32 -22
  13. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/contributor/index.md +2 -0
  14. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/phylogenetics-workflow.md +1 -1
  15. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/qc-profiles.md +6 -3
  16. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/remote-execution.md +3 -2
  17. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/behaviors-and-limitations.md +21 -37
  18. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-analysis.md +7 -5
  19. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-decisions-reports.md +4 -4
  20. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-tui.md +47 -7
  21. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/index.md +2 -0
  22. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/recipe-fields.md +21 -2
  23. operondbs-0.8.2/docs/en/reference/resolved-issues.md +58 -0
  24. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/external-analysis.md +44 -5
  25. operondbs-0.8.2/docs/zh/contributor/defect-tracking.md +55 -0
  26. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/contributor/development-testing.md +18 -15
  27. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/contributor/index.md +2 -0
  28. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/phylogenetics-workflow.md +1 -1
  29. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/qc-profiles.md +10 -5
  30. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/remote-execution.md +11 -3
  31. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/behaviors-and-limitations.md +19 -35
  32. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-analysis.md +20 -5
  33. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-decisions-reports.md +13 -8
  34. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-tui.md +38 -7
  35. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/index.md +2 -0
  36. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/recipe-fields.md +30 -3
  37. operondbs-0.8.2/docs/zh/reference/resolved-issues.md +50 -0
  38. {operondbs-0.8.0 → operondbs-0.8.2}/operon/adapters/ncbi_datasets.py +13 -10
  39. {operondbs-0.8.0 → operondbs-0.8.2}/operon/classify.py +36 -14
  40. {operondbs-0.8.0 → operondbs-0.8.2}/operon/cli.py +15 -4
  41. {operondbs-0.8.0 → operondbs-0.8.2}/operon/coverage.py +3 -3
  42. {operondbs-0.8.0 → operondbs-0.8.2}/operon/database.py +51 -37
  43. {operondbs-0.8.0 → operondbs-0.8.2}/operon/entity_view.py +9 -9
  44. {operondbs-0.8.0 → operondbs-0.8.2}/operon/environment_capture.py +44 -1
  45. {operondbs-0.8.0 → operondbs-0.8.2}/operon/execution.py +607 -62
  46. {operondbs-0.8.0 → operondbs-0.8.2}/operon/export.py +41 -31
  47. {operondbs-0.8.0 → operondbs-0.8.2}/operon/fanout.py +29 -22
  48. {operondbs-0.8.0 → operondbs-0.8.2}/operon/files.py +40 -12
  49. {operondbs-0.8.0 → operondbs-0.8.2}/operon/import_wizard.py +3 -1
  50. {operondbs-0.8.0 → operondbs-0.8.2}/operon/lifecycle.py +5 -5
  51. {operondbs-0.8.0 → operondbs-0.8.2}/operon/qc/__init__.py +6 -3
  52. {operondbs-0.8.0 → operondbs-0.8.2}/operon/release.py +33 -6
  53. {operondbs-0.8.0 → operondbs-0.8.2}/operon/remotes.py +24 -7
  54. {operondbs-0.8.0 → operondbs-0.8.2}/operon/rules.py +7 -6
  55. {operondbs-0.8.0 → operondbs-0.8.2}/operon/sequence_tools.py +2 -2
  56. operondbs-0.8.2/operon/sql.py +18 -0
  57. {operondbs-0.8.0 → operondbs-0.8.2}/operon/table_import.py +8 -3
  58. {operondbs-0.8.0 → operondbs-0.8.2}/operon/taxonomy.py +4 -4
  59. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tools.py +526 -102
  60. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/actions.py +29 -7
  61. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/data.py +38 -15
  62. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/config.py +25 -10
  63. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/files_ops.py +51 -3
  64. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/import_wizard.py +75 -19
  65. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/publish.py +9 -1
  66. {operondbs-0.8.0 → operondbs-0.8.2}/operon/workflow.py +118 -3
  67. {operondbs-0.8.0 → operondbs-0.8.2}/pyproject.toml +7 -4
  68. operondbs-0.8.2/tests/integration/test_analysis_array.py +774 -0
  69. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_analysis_shutdown.py +25 -0
  70. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_classify_sequences.py +45 -0
  71. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_fanout_analyze.py +31 -0
  72. operondbs-0.8.2/tests/regression/test_sql_identifier_security.py +124 -0
  73. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_classify.py +34 -2
  74. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_core_coverage_more.py +7 -0
  75. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_database_edges_more.py +24 -0
  76. operondbs-0.8.2/tests/unit/test_defect_registry.py +121 -0
  77. operondbs-0.8.2/tests/unit/test_defects_cli.py +150 -0
  78. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_environment_capture.py +73 -1
  79. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_execution.py +361 -2
  80. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_execution_edges.py +301 -5
  81. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_export.py +62 -0
  82. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_fanout.py +109 -5
  83. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_files_edges.py +92 -3
  84. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_misc_coverage_more.py +5 -1
  85. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_qc_and_rules.py +37 -0
  86. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_remote_taxonomy_more.py +5 -2
  87. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_remotes.py +2 -2
  88. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tools_execution_more.py +4 -0
  89. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tui_config.py +93 -14
  90. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tui_publish.py +197 -2
  91. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tui_screens_more.py +2 -2
  92. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tui_writes.py +76 -0
  93. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_views_release_reports_edges.py +60 -0
  94. {operondbs-0.8.0 → operondbs-0.8.2}/.readthedocs.yaml +0 -0
  95. {operondbs-0.8.0 → operondbs-0.8.2}/LICENSE +0 -0
  96. {operondbs-0.8.0 → operondbs-0.8.2}/MANIFEST.in +0 -0
  97. {operondbs-0.8.0 → operondbs-0.8.2}/OperonDBS.egg-info/dependency_links.txt +0 -0
  98. {operondbs-0.8.0 → operondbs-0.8.2}/OperonDBS.egg-info/entry_points.txt +0 -0
  99. {operondbs-0.8.0 → operondbs-0.8.2}/OperonDBS.egg-info/requires.txt +0 -0
  100. {operondbs-0.8.0 → operondbs-0.8.2}/OperonDBS.egg-info/top_level.txt +0 -0
  101. {operondbs-0.8.0 → operondbs-0.8.2}/benchmarks/qc_representative_entities.tsv +0 -0
  102. {operondbs-0.8.0 → operondbs-0.8.2}/docs/_static/language-switcher.js +0 -0
  103. {operondbs-0.8.0 → operondbs-0.8.2}/docs/_static/operon.css +0 -0
  104. {operondbs-0.8.0 → operondbs-0.8.2}/docs/_templates/layout.html +0 -0
  105. {operondbs-0.8.0 → operondbs-0.8.2}/docs/conf.py +0 -0
  106. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/extensibility.md +0 -0
  107. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/files-and-storage.md +0 -0
  108. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/index.md +0 -0
  109. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/metadata-and-data-model.md +0 -0
  110. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/overview.md +0 -0
  111. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/qc-and-rules.md +0 -0
  112. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/release-lifecycle.md +0 -0
  113. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/architecture/taxonomy-coverage.md +0 -0
  114. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/contributor/documentation-deployment.md +0 -0
  115. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/contributor/pypi-release.md +0 -0
  116. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/contributor/repository-guide.md +0 -0
  117. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/getting-started/daily-workflow.md +0 -0
  118. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/getting-started/first-project.md +0 -0
  119. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/getting-started/index.md +0 -0
  120. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/getting-started/installation.md +0 -0
  121. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/getting-started/quickstart.md +0 -0
  122. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/backup-migration.md +0 -0
  123. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/curation-lifecycle.md +0 -0
  124. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/external-analysis.md +0 -0
  125. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/file-archiving.md +0 -0
  126. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/index.md +0 -0
  127. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/metadata-import.md +0 -0
  128. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/ncbi-datasets.md +0 -0
  129. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/remote-first.md +0 -0
  130. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/remote-storage.md +0 -0
  131. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/taxonomy-coverage.md +0 -0
  132. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/guides/troubleshooting.md +0 -0
  133. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/index.md +0 -0
  134. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/operations/database-compatibility.md +0 -0
  135. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/operations/index.md +0 -0
  136. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/operations/ncbi-recovery-migration.md +0 -0
  137. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/operations/qc-performance.md +0 -0
  138. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/overview.md +0 -0
  139. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-files-qc.md +0 -0
  140. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-project-metadata.md +0 -0
  141. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-remote.md +0 -0
  142. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-taxonomy-lifecycle-admin.md +0 -0
  143. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/cli-workflow.md +0 -0
  144. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/data-model.md +0 -0
  145. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/recipe-overview.md +0 -0
  146. {operondbs-0.8.0 → operondbs-0.8.2}/docs/en/reference/recipe-parsers-examples.md +0 -0
  147. {operondbs-0.8.0 → operondbs-0.8.2}/docs/index.md +0 -0
  148. {operondbs-0.8.0 → operondbs-0.8.2}/docs/requirements.txt +0 -0
  149. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/extensibility.md +0 -0
  150. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/files-and-storage.md +0 -0
  151. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/index.md +0 -0
  152. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/metadata-and-data-model.md +0 -0
  153. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/overview.md +0 -0
  154. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/qc-and-rules.md +0 -0
  155. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/release-lifecycle.md +0 -0
  156. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/architecture/taxonomy-coverage.md +0 -0
  157. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/contributor/documentation-deployment.md +0 -0
  158. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/contributor/pypi-release.md +0 -0
  159. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/contributor/repository-guide.md +0 -0
  160. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/getting-started/daily-workflow.md +0 -0
  161. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/getting-started/first-project.md +0 -0
  162. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/getting-started/index.md +0 -0
  163. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/getting-started/installation.md +0 -0
  164. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/getting-started/quickstart.md +0 -0
  165. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/backup-migration.md +0 -0
  166. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/curation-lifecycle.md +0 -0
  167. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/external-analysis.md +0 -0
  168. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/file-archiving.md +0 -0
  169. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/index.md +0 -0
  170. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/metadata-import.md +0 -0
  171. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/ncbi-datasets.md +0 -0
  172. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/remote-first.md +0 -0
  173. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/remote-storage.md +0 -0
  174. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/taxonomy-coverage.md +0 -0
  175. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/guides/troubleshooting.md +0 -0
  176. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/index.md +0 -0
  177. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/operations/database-compatibility.md +0 -0
  178. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/operations/index.md +0 -0
  179. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/operations/ncbi-recovery-migration.md +0 -0
  180. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/operations/qc-performance.md +0 -0
  181. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/overview.md +0 -0
  182. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-files-qc.md +0 -0
  183. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-project-metadata.md +0 -0
  184. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-remote.md +0 -0
  185. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-taxonomy-lifecycle-admin.md +0 -0
  186. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/cli-workflow.md +0 -0
  187. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/data-model.md +0 -0
  188. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/recipe-overview.md +0 -0
  189. {operondbs-0.8.0 → operondbs-0.8.2}/docs/zh/reference/recipe-parsers-examples.md +0 -0
  190. {operondbs-0.8.0 → operondbs-0.8.2}/operon/__init__.py +0 -0
  191. {operondbs-0.8.0 → operondbs-0.8.2}/operon/__main__.py +0 -0
  192. {operondbs-0.8.0 → operondbs-0.8.2}/operon/adapters/__init__.py +0 -0
  193. {operondbs-0.8.0 → operondbs-0.8.2}/operon/adapters/timetree.py +0 -0
  194. {operondbs-0.8.0 → operondbs-0.8.2}/operon/backup.py +0 -0
  195. {operondbs-0.8.0 → operondbs-0.8.2}/operon/config.py +0 -0
  196. {operondbs-0.8.0 → operondbs-0.8.2}/operon/demo.py +0 -0
  197. {operondbs-0.8.0 → operondbs-0.8.2}/operon/environment.py +0 -0
  198. {operondbs-0.8.0 → operondbs-0.8.2}/operon/errors.py +0 -0
  199. {operondbs-0.8.0 → operondbs-0.8.2}/operon/lineage.py +0 -0
  200. {operondbs-0.8.0 → operondbs-0.8.2}/operon/metadata_files.py +0 -0
  201. {operondbs-0.8.0 → operondbs-0.8.2}/operon/ncbi_reconcile.py +0 -0
  202. {operondbs-0.8.0 → operondbs-0.8.2}/operon/profiles.py +0 -0
  203. {operondbs-0.8.0 → operondbs-0.8.2}/operon/qc/_alignment.pyx +0 -0
  204. {operondbs-0.8.0 → operondbs-0.8.2}/operon/qc/_parsers.pyx +0 -0
  205. {operondbs-0.8.0 → operondbs-0.8.2}/operon/qc/alignment.py +0 -0
  206. {operondbs-0.8.0 → operondbs-0.8.2}/operon/qc/measure.py +0 -0
  207. {operondbs-0.8.0 → operondbs-0.8.2}/operon/qc/parsers.py +0 -0
  208. {operondbs-0.8.0 → operondbs-0.8.2}/operon/reports.py +0 -0
  209. {operondbs-0.8.0 → operondbs-0.8.2}/operon/schema.py +0 -0
  210. {operondbs-0.8.0 → operondbs-0.8.2}/operon/shutdown.py +0 -0
  211. {operondbs-0.8.0 → operondbs-0.8.2}/operon/timetree.py +0 -0
  212. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/__init__.py +0 -0
  213. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/app.py +0 -0
  214. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/app.tcss +0 -0
  215. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/assets/splash.png +0 -0
  216. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/assets/splash.rgb.z +0 -0
  217. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/__init__.py +0 -0
  218. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/common.py +0 -0
  219. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/coverage.py +0 -0
  220. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/decisions.py +0 -0
  221. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/entities.py +0 -0
  222. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/files.py +0 -0
  223. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/home.py +0 -0
  224. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/screens/runs.py +0 -0
  225. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/splash.py +0 -0
  226. {operondbs-0.8.0 → operondbs-0.8.2}/operon/tui/splash_terminal.py +0 -0
  227. {operondbs-0.8.0 → operondbs-0.8.2}/operon/utils.py +0 -0
  228. {operondbs-0.8.0 → operondbs-0.8.2}/setup.cfg +0 -0
  229. {operondbs-0.8.0 → operondbs-0.8.2}/setup.py +0 -0
  230. {operondbs-0.8.0 → operondbs-0.8.2}/tests/__init__.py +0 -0
  231. {operondbs-0.8.0 → operondbs-0.8.2}/tests/compatibility/__init__.py +0 -0
  232. {operondbs-0.8.0 → operondbs-0.8.2}/tests/compatibility/test_python_support.py +0 -0
  233. {operondbs-0.8.0 → operondbs-0.8.2}/tests/helpers.py +0 -0
  234. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/__init__.py +0 -0
  235. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_analysis_resume.py +0 -0
  236. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_analysis_tools.py +0 -0
  237. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_execution_backends.py +0 -0
  238. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_lineage_cascade.py +0 -0
  239. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_ncbi_datasets_adapter.py +0 -0
  240. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_pipeline_and_release.py +0 -0
  241. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_python_packaging.py +0 -0
  242. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_qc_remote_measure.py +0 -0
  243. {operondbs-0.8.0 → operondbs-0.8.2}/tests/integration/test_taxonomy_coverage.py +0 -0
  244. {operondbs-0.8.0 → operondbs-0.8.2}/tests/regression/__init__.py +0 -0
  245. {operondbs-0.8.0 → operondbs-0.8.2}/tests/regression/test_correctness.py +0 -0
  246. {operondbs-0.8.0 → operondbs-0.8.2}/tests/regression/test_cython_alignment_parity.py +0 -0
  247. {operondbs-0.8.0 → operondbs-0.8.2}/tests/regression/test_cython_parser_parity.py +0 -0
  248. {operondbs-0.8.0 → operondbs-0.8.2}/tests/regression/test_parser_semantics.py +0 -0
  249. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/__init__.py +0 -0
  250. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_alignment_qc.py +0 -0
  251. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_cli_edges.py +0 -0
  252. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_config_workflow_edges.py +0 -0
  253. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_coverage_edges.py +0 -0
  254. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_docs_versions.py +0 -0
  255. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_environment.py +0 -0
  256. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_follow.py +0 -0
  257. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_import_backup_show.py +0 -0
  258. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_import_wizard_edges.py +0 -0
  259. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_lifecycle.py +0 -0
  260. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_lineage.py +0 -0
  261. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_ncbi_coverage_gaps.py +0 -0
  262. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_ncbi_edge_cases.py +0 -0
  263. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_ncbi_reconcile_edges.py +0 -0
  264. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_parser_edge_paths.py +0 -0
  265. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_qc_edges_more.py +0 -0
  266. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_qc_measure.py +0 -0
  267. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_qc_remote_only.py +0 -0
  268. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_recipe_history.py +0 -0
  269. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_remote_transport_edges.py +0 -0
  270. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_remotes_edges.py +0 -0
  271. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_rules_schema_edges.py +0 -0
  272. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_schema_2_10.py +0 -0
  273. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_schema_2_11.py +0 -0
  274. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_schema_2_9.py +0 -0
  275. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_schema_and_metadata.py +0 -0
  276. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_sequence_tools.py +0 -0
  277. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_sequences.py +0 -0
  278. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_shutdown.py +0 -0
  279. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_support_edges.py +0 -0
  280. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_table_import_edges.py +0 -0
  281. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_taxonomy_edges.py +0 -0
  282. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_timetree.py +0 -0
  283. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_timetree_snapshots.py +0 -0
  284. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tools_edges.py +0 -0
  285. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tui.py +0 -0
  286. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tui_flows_more.py +0 -0
  287. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_tui_splash_terminal.py +0 -0
  288. {operondbs-0.8.0 → operondbs-0.8.2}/tests/unit/test_workflow_cli.py +0 -0
@@ -71,7 +71,7 @@ jobs:
71
71
  - run: python -m cibuildwheel --output-dir wheelhouse
72
72
  env:
73
73
  CIBW_ARCHS: ${{ matrix.architecture }}
74
- CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*"
74
+ CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-* cp315-*"
75
75
  CIBW_SKIP: "*-musllinux_*"
76
76
  CIBW_TEST_COMMAND: >-
77
77
  python -c "import operon; import operon.qc._parsers;
@@ -32,20 +32,21 @@ jobs:
32
32
  fail-fast: false
33
33
  matrix:
34
34
  os: [ ubuntu-latest, macos-latest ]
35
- python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
35
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
36
36
  runs-on: ${{ matrix.os }}
37
37
  steps:
38
38
  - uses: actions/checkout@v6
39
39
  - uses: actions/setup-python@v7
40
40
  with:
41
+ allow-prereleases: true
42
+ check-latest: true
41
43
  python-version: ${{ matrix.python-version }}
42
44
  cache: pip
43
45
  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
46
+ - run: |
47
+ python -m pip install --upgrade pip
48
+ python -m pip install -e '.[test]'
49
+ python -m pytest
50
+ - uses: codacy/codacy-coverage-reporter-action@v1.3.0
49
51
  with:
50
- token: ${{ secrets.CODECOV_TOKEN }}
51
- report_type: test_results
52
+ project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
@@ -31,3 +31,4 @@ htmlcov/
31
31
  /workflows/
32
32
  /HPC/
33
33
  .matrix/
34
+ cobertura.xml
@@ -26,7 +26,7 @@ principle-to-implementation mapping.
26
26
 
27
27
  Current version markers (must stay consistent across code and docs):
28
28
 
29
- - `operon` 0.8.0 (`pyproject.toml`)
29
+ - `operon` 0.8.2 (`pyproject.toml`)
30
30
  - database schema 2.11 (`operon/database.py`, `SCHEMA_VERSION`)
31
31
  - metadata schema 1.4 (`operon/schema.py`, `METADATA_SCHEMA_VERSION`)
32
32
 
@@ -133,7 +133,9 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
133
133
  (see `docs/*/operations/qc-performance.md`).
134
134
  - `scripts/` — local developer tooling; `setup-test-matrix.sh` and
135
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`).
136
+ matrix under .matrix/ (see `docs/*/contributor/development-testing.md`);
137
+ `defects.sh` appends to and queries the defect registry (`defects.yml`,
138
+ see "Defect reports" below).
137
139
 
138
140
  ## Setup, test, and build
139
141
 
@@ -152,7 +154,7 @@ python -m pytest tests/unit # by category: unit / integration /
152
154
 
153
155
  python -m pytest --lf -q --no-cov # iterate: last failures, no coverage
154
156
  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
157
+ scripts/run-test-matrix.sh # whole suite on 3.10-3.15 concurrently
156
158
 
157
159
  python setup.py build_ext --inplace # rebuild only the Cython extension
158
160
 
@@ -160,12 +162,33 @@ sphinx-build -W --keep-going -b html docs docs/_build/html # strict docs build
160
162
  ```
161
163
 
162
164
  Run the relevant test category after any change; run the full suite before
163
- considering work done. CI (`.github/workflows/test.yml`) runs pytest on
164
- Python 3.10–3.14 and the strict Sphinx build. Releases are published
165
- exclusively to PyPI; see `docs/*/contributor/pypi-release.md`.
165
+ considering work done.
166
166
 
167
- Do not commit or perform other git mutations unless the user explicitly
168
- asks.
167
+ CI (`.github/workflows/test.yml`) runs pytest on Python 3.10–3.15 and the strict
168
+ Sphinx build. Releases are published exclusively to PyPI; see `docs/*/contributor/pypi-release.md`.
169
+
170
+ The project uses `pytest-xdist` to parallel testing. Avoid sharing state between tests
171
+ to prevent unexpected or random test results.
172
+
173
+ > Running `python -m pytest` directly takes approx 35 seconds
174
+ > (auto: 24 workers). Running the local test matrix takes 3 minutes
175
+ > (6 jobs × 4 workers), or 4~5min (6 jobs × 2 workers in default).
176
+ >
177
+ > Measured on Intel Core i7-13700HX (16c24t).
178
+
179
+ ## Git
180
+
181
+ If the user requests or grants permission, do `git commit` after modify.
182
+ Avoid including overly large changes in a single commit. For commit messages,
183
+ follow the pattern used in the last five commits; otherwise, the Conventional
184
+ Commits guidelines.
185
+
186
+ **DO NOT** push any commit or tag unless the user **explicitly** requests that.
187
+
188
+ If times out, assume the user is not present and that a GPG signature
189
+ is required. Add `-c commit.gpgsign=false` behind `git` may help (However,
190
+ re-signing will reset the commit hash, and corrupt the defects.yml. Please
191
+ proceed with caution).
169
192
 
170
193
  ## Conventions
171
194
 
@@ -203,6 +226,22 @@ asks.
203
226
  and an F821 error is likely to occur. Globally ignoring F821 is **STRICKLY
204
227
  PROHIBITED**.
205
228
 
229
+ ## Defect reports
230
+
231
+ Confirmed defects are tracked in the machine-readable registry
232
+ `defects.yml` at the repository root (plus any `defects/*.yml` shards;
233
+ `scripts/defects.sh` appends and queries records). The full process is in
234
+ `docs/*/contributor/defect-tracking.md`; the binding rules are:
235
+
236
+ 1. **Register first.** When an audit or investigation confirms a defect,
237
+ append its `ODR-XXXX` record to `defects.yml` before the fix lands.
238
+ 2. **One commit per defect.** The fix and its regression tests ride in the
239
+ same commit; fill in `fix_commit` when committing.
240
+ 3. **Close the test loop.** Regression tests for a defect carry
241
+ `@pytest.mark.bug("ODR-XXXX")`, and every `fixed`/`verified` record
242
+ lists at least one such test; `tests/unit/test_defect_registry.py`
243
+ validates the registry schema and both directions of this closure.
244
+
206
245
  ## Documentation sync
207
246
 
208
247
  When you change behavior, CLI surface, configuration fields, or storage
@@ -218,7 +257,7 @@ change:
218
257
  - Contributor-facing processes → `docs/*/contributor/`; navigation →
219
258
  `docs/*/index.md`
220
259
 
221
- Version markers in docs (`operon` 0.8.0, database schema 2.11, metadata
260
+ Version markers in docs (`operon` 0.8.2, database schema 2.11, metadata
222
261
  schema 1.4) must match `pyproject.toml` and the code. Do not write the
223
262
  current values literally in Markdown sources: use the `myst_substitutions`
224
263
  references `{{ operon_version }}`, `{{ db_schema }}`, and
@@ -235,6 +274,9 @@ Sphinx-rendered); update the list above when bumping.
235
274
 
236
275
  ## Special Note For Codex/ChatGPT
237
276
 
238
- Due to specific limitations of the sandbox environment, executing certain TUI test code may cause the system to freeze; this often occurs on Codex/ChatGPT. The specific reason is that when this test code runs in the sandbox, it may results a Textual/asyncio cleanup block, and reports "FAIL" due to timeout.
277
+ Due to specific limitations of the sandbox environment, executing certain TUI
278
+ test code may cause the system to freeze; this often occurs on Codex/ChatGPT.
279
+ The specific reason is that when this test code runs in the sandbox, it may
280
+ result a Textual/asyncio cleanup block, and reports "FAIL" due to timeout.
239
281
 
240
282
  If you are Codex/ChatGPT, please execute TUI-related test code OUTSIDE the sandbox.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OperonDBS
3
- Version: 0.8.0
3
+ Version: 0.8.2
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
@@ -46,9 +46,14 @@ Requires-Dist: myst-parser<5,>=4.0; extra == "dev"
46
46
  Requires-Dist: sphinx-rtd-theme<4,>=3.0; extra == "dev"
47
47
  Dynamic: license-file
48
48
 
49
- # Operon
49
+ # Operon the Database System
50
50
 
51
- [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/HYLi360/Operon/branch/master/graph/badge.svg?token=BC4LD8UPL2)](https://codecov.io/gh/HYLi360/Operon)
51
+ [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml)
52
+ [![RtD badge](https://img.shields.io/badge/Read-the_Document-blue?style=flat)](https://operonproject.readthedocs.io/)
53
+ ![PyPI Version](https://img.shields.io/pypi/v/OperonDBS?link=https%3A%2F%2Fpypi.org%2Fproject%2FOperonDBS%2F)
54
+ ![GitHub commit activity](https://img.shields.io/github/commit-activity/t/HYLi360/Operon?logo=github)
55
+ [![Codacy Code quality Badge](https://app.codacy.com/project/badge/Grade/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
56
+ [![Codacy Coverage Badge](https://app.codacy.com/project/badge/Coverage/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
52
57
 
53
58
  A Python-based, **file-based database** designed for large-scale genomic data, used for archiving, quality control, analysis, and deterministic automation.
54
59
 
@@ -33,6 +33,7 @@ docs/en/architecture/overview.md
33
33
  docs/en/architecture/qc-and-rules.md
34
34
  docs/en/architecture/release-lifecycle.md
35
35
  docs/en/architecture/taxonomy-coverage.md
36
+ docs/en/contributor/defect-tracking.md
36
37
  docs/en/contributor/development-testing.md
37
38
  docs/en/contributor/documentation-deployment.md
38
39
  docs/en/contributor/index.md
@@ -75,6 +76,7 @@ docs/en/reference/index.md
75
76
  docs/en/reference/recipe-fields.md
76
77
  docs/en/reference/recipe-overview.md
77
78
  docs/en/reference/recipe-parsers-examples.md
79
+ docs/en/reference/resolved-issues.md
78
80
  docs/zh/index.md
79
81
  docs/zh/overview.md
80
82
  docs/zh/architecture/extensibility.md
@@ -86,6 +88,7 @@ docs/zh/architecture/overview.md
86
88
  docs/zh/architecture/qc-and-rules.md
87
89
  docs/zh/architecture/release-lifecycle.md
88
90
  docs/zh/architecture/taxonomy-coverage.md
91
+ docs/zh/contributor/defect-tracking.md
89
92
  docs/zh/contributor/development-testing.md
90
93
  docs/zh/contributor/documentation-deployment.md
91
94
  docs/zh/contributor/index.md
@@ -128,6 +131,7 @@ docs/zh/reference/index.md
128
131
  docs/zh/reference/recipe-fields.md
129
132
  docs/zh/reference/recipe-overview.md
130
133
  docs/zh/reference/recipe-parsers-examples.md
134
+ docs/zh/reference/resolved-issues.md
131
135
  operon/__init__.py
132
136
  operon/__main__.py
133
137
  operon/backup.py
@@ -158,6 +162,7 @@ operon/rules.py
158
162
  operon/schema.py
159
163
  operon/sequence_tools.py
160
164
  operon/shutdown.py
165
+ operon/sql.py
161
166
  operon/table_import.py
162
167
  operon/taxonomy.py
163
168
  operon/timetree.py
@@ -199,6 +204,7 @@ tests/helpers.py
199
204
  tests/compatibility/__init__.py
200
205
  tests/compatibility/test_python_support.py
201
206
  tests/integration/__init__.py
207
+ tests/integration/test_analysis_array.py
202
208
  tests/integration/test_analysis_resume.py
203
209
  tests/integration/test_analysis_shutdown.py
204
210
  tests/integration/test_analysis_tools.py
@@ -216,6 +222,7 @@ tests/regression/test_correctness.py
216
222
  tests/regression/test_cython_alignment_parity.py
217
223
  tests/regression/test_cython_parser_parity.py
218
224
  tests/regression/test_parser_semantics.py
225
+ tests/regression/test_sql_identifier_security.py
219
226
  tests/unit/__init__.py
220
227
  tests/unit/test_alignment_qc.py
221
228
  tests/unit/test_classify.py
@@ -224,6 +231,8 @@ tests/unit/test_config_workflow_edges.py
224
231
  tests/unit/test_core_coverage_more.py
225
232
  tests/unit/test_coverage_edges.py
226
233
  tests/unit/test_database_edges_more.py
234
+ tests/unit/test_defect_registry.py
235
+ tests/unit/test_defects_cli.py
227
236
  tests/unit/test_docs_versions.py
228
237
  tests/unit/test_environment.py
229
238
  tests/unit/test_environment_capture.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OperonDBS
3
- Version: 0.8.0
3
+ Version: 0.8.2
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
@@ -46,9 +46,14 @@ Requires-Dist: myst-parser<5,>=4.0; extra == "dev"
46
46
  Requires-Dist: sphinx-rtd-theme<4,>=3.0; extra == "dev"
47
47
  Dynamic: license-file
48
48
 
49
- # Operon
49
+ # Operon the Database System
50
50
 
51
- [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/HYLi360/Operon/branch/master/graph/badge.svg?token=BC4LD8UPL2)](https://codecov.io/gh/HYLi360/Operon)
51
+ [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml)
52
+ [![RtD badge](https://img.shields.io/badge/Read-the_Document-blue?style=flat)](https://operonproject.readthedocs.io/)
53
+ ![PyPI Version](https://img.shields.io/pypi/v/OperonDBS?link=https%3A%2F%2Fpypi.org%2Fproject%2FOperonDBS%2F)
54
+ ![GitHub commit activity](https://img.shields.io/github/commit-activity/t/HYLi360/Operon?logo=github)
55
+ [![Codacy Code quality Badge](https://app.codacy.com/project/badge/Grade/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
56
+ [![Codacy Coverage Badge](https://app.codacy.com/project/badge/Coverage/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
52
57
 
53
58
  A Python-based, **file-based database** designed for large-scale genomic data, used for archiving, quality control, analysis, and deterministic automation.
54
59
 
@@ -1,6 +1,11 @@
1
- # Operon
2
-
3
- [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/HYLi360/Operon/branch/master/graph/badge.svg?token=BC4LD8UPL2)](https://codecov.io/gh/HYLi360/Operon)
1
+ # Operon the Database System
2
+
3
+ [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml)
4
+ [![RtD badge](https://img.shields.io/badge/Read-the_Document-blue?style=flat)](https://operonproject.readthedocs.io/)
5
+ ![PyPI Version](https://img.shields.io/pypi/v/OperonDBS?link=https%3A%2F%2Fpypi.org%2Fproject%2FOperonDBS%2F)
6
+ ![GitHub commit activity](https://img.shields.io/github/commit-activity/t/HYLi360/Operon?logo=github)
7
+ [![Codacy Code quality Badge](https://app.codacy.com/project/badge/Grade/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
8
+ [![Codacy Coverage Badge](https://app.codacy.com/project/badge/Coverage/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
4
9
 
5
10
  A Python-based, **file-based database** designed for large-scale genomic data, used for archiving, quality control, analysis, and deterministic automation.
6
11
 
@@ -1,6 +1,11 @@
1
- # Operon
2
-
3
- [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/HYLi360/Operon/branch/master/graph/badge.svg?token=BC4LD8UPL2)](https://codecov.io/gh/HYLi360/Operon)
1
+ # Operon the Database System
2
+
3
+ [![test status](https://github.com/HYLi360/Operon/actions/workflows/test.yml/badge.svg)](https://github.com/HYLi360/Operon/actions/workflows/test.yml)
4
+ [![RtD badge](https://img.shields.io/badge/Read-the_Document-blue?style=flat)](https://operonproject.readthedocs.io/)
5
+ ![PyPI Version](https://img.shields.io/pypi/v/OperonDBS?link=https%3A%2F%2Fpypi.org%2Fproject%2FOperonDBS%2F)
6
+ ![GitHub commit activity](https://img.shields.io/github/commit-activity/t/HYLi360/Operon?logo=github)
7
+ [![Codacy Code quality Badge](https://app.codacy.com/project/badge/Grade/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
8
+ [![Codacy Coverage Badge](https://app.codacy.com/project/badge/Coverage/47d652651ff645ff86fa0646768a08b3)](https://app.codacy.com/gh/HYLi360/Operon/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
4
9
 
5
10
  一个基于 Python 的、面向大规模基因组数据的**基于文件的数据库**,用于归档、质控、分析与确定性自动化处理。
6
11
 
@@ -20,13 +20,13 @@ Actual execution of external commands is taken over by the backend abstraction i
20
20
 
21
21
  - `local` (default): the original local subprocess behavior, completely unchanged;
22
22
  - `slurm`: a local Slurm cluster. Generates a `<run_id>.sbatch` batch script under `logs/` (`--cpus-per-task` takes the thread count; optional `--time`/`--partition`/`--mem`, `extra_sbatch`, and `setup_commands`), submits with `sbatch --parsable`, polls `squeue` at `poll_interval`, and after the job disappears reads the `<run_id>.exitcode` file written by the script (falling back to `sacct` on failure); requires the project directory to be on a filesystem shared with the compute nodes;
23
- - `ssh`: executes on an SSH remote host (HPC head node / cloud VM) through the lazily imported Paramiko runtime dependency; with `execution.ssh.scheduler: slurm`, sbatch/squeue runs on the remote instead. Supports `remote_root` path mapping (empty means a shared filesystem); input files are uploaded over SFTP (identical content skipped, strict SHA-256/directory-tree hash, different content refused). When `storage_remote` is configured, REMOTE_ONLY inputs are consumed in place on the remote. Before a run, the exactly computed old remote output is removed; expected outputs are pulled back through temporary files and compared against the remote content again; an existing local output is accepted only when its content is identical. `storage_remote` and an explicit `remote_root` must point to the same root. One analysis batch uses a single lazy SSH client for version probing, remote input verification, database preflight, and all commands, closing it uniformly at the end. Direct commands run in a separate process group via util-linux `setsid --wait` (guaranteeing reliable exit-code return); on timeout, TERM/KILL is sent to the remote process group based on a restricted PID file, and when a termination request cannot be sent, the error and provenance state explicitly that the process may still be running. Remote Slurm polls strictly at `poll_interval` and retries the exitcode file that is briefly invisible after a job disappears.
23
+ - `ssh`: executes on an SSH remote host (HPC head node / cloud VM) through the lazily imported Paramiko runtime dependency; with `execution.ssh.scheduler: slurm`, sbatch/squeue runs on the remote instead. Supports `remote_root` path mapping (empty means a shared filesystem); input files are uploaded over SFTP (identical content skipped, strict SHA-256/directory-tree hash, different content refused). When `storage_remote` is configured, REMOTE_ONLY inputs are consumed in place on the remote. Before a run, an existing remote output is renamed to a `<path>.operon-prev-<uuid>` backup rather than deleted; the backup is dropped once the run succeeds and the new output verifies, and a failed or interrupted run best-effort restores it in place. Expected outputs are pulled back through temporary files and compared against the remote content again; an existing local output is accepted only when its content is identical. `storage_remote` and an explicit `remote_root` must point to the same root. One analysis batch uses a single lazy SSH client for version probing, remote input verification, database preflight, and all commands, closing it uniformly at the end. Direct commands run in a separate process group via util-linux `setsid --wait` (guaranteeing reliable exit-code return); on timeout, TERM/KILL is sent to the remote process group based on a restricted PID file, and when a termination request cannot be sent, the error and provenance state explicitly that the process may still be running. Remote Slurm polls strictly at `poll_interval` and retries the exitcode file that is briefly invisible after a job disappears.
24
24
 
25
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).
26
26
 
27
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
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.
29
+ The local, Slurm and SSH backends probe immediately before the payload. The local probe is memoized in-process per (launcher, working directory) for 300 seconds (`_LOCAL_CAPTURE_TTL_SECONDS`), so a batch of commands through one launcher pays for one probe; failed captures are not cached. 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
30
 
31
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
32
 
@@ -48,6 +48,16 @@ Whenever a backend cannot collect a metric the column stays NULL — no error is
48
48
 
49
49
  `run-external` provenance is aligned with `analyze`: `--tool NAME` matching a tool configured in `config/tools.yaml` triggers automatic version detection recorded as `tool_version` and `tool_version_raw` (a failed probe degrades to a warning and does not block the run); `--input PATH` (repeatable) declares input files whose per-file SHA-256 values are combined into `input_sha256`, with the full list recorded in `execution_details`; `--threads` records the thread count requested from the execution backend.
50
50
 
51
- Graceful shutdown (`shutdown.py`): while an `analyze` batch is running, SIGINT/SIGTERM handlers are installed; a signal is converted into `ShutdownRequested` (a `KeyboardInterrupt` subclass) raised on the main thread, and cleanup follows the normal exception path — `local` backend subprocesses start with `start_new_session` in their own process group, and on interruption/timeout the whole process group (including grandchildren) receives TERM then KILL; the `slurm` and remote-Slurm backends `scancel` the current job on interruption; the `ssh` direct backend reuses the timeout path's remote process-group TERM/KILL. The current `analysis_jobs` row is set to `interrupted` (it does not participate in completed-cache hits), half-finished output is deleted by default (`--keep-partial` keeps it), and the batch then terminates with exit code 130; a second signal during cleanup forces an immediate exit. A `RUNNING` row left behind by a SIGKILLed process is swept to `interrupted` at the next `analyze` startup, so resume semantics always hold.
51
+ Graceful shutdown (`shutdown.py`): while an `analyze` batch is running, SIGINT/SIGTERM handlers are installed; a signal is converted into `ShutdownRequested` (a `KeyboardInterrupt` subclass) raised on the main thread, and cleanup follows the normal exception path — `local` backend subprocesses start with `start_new_session` in their own process group, and on interruption/timeout the whole process group (including grandchildren) receives TERM then KILL; the `slurm` and remote-Slurm backends `scancel` the current job on interruption; the `ssh` direct backend reuses the timeout path's remote process-group TERM/KILL. The current `analysis_jobs` row is set to `interrupted` (it does not participate in completed-cache hits), half-finished output is deleted by default (`--keep-partial` keeps it), and the batch then terminates with exit code 130; a second signal during cleanup forces an immediate exit. A `RUNNING` row of the same analysis left behind by a SIGKILLed process is swept to `interrupted` at the next `analyze` startup, so resume semantics always hold.
52
+
53
+ ## Slurm array submission
54
+
55
+ On HPC clusters, submitting N files of one recipe as N separate scheduler jobs pays N times the queue and accounting overhead for what is logically one batch. When a recipe opts in (`slurm.array: true`), the selected executor implements array submission, and at least two files of one `analyze` batch miss the cache, `analyze` instead submits them as a single Slurm job array — one `sbatch`, one `squeue` poll loop — with each file occupying one array task. The `local` backend and direct SSH (`scheduler: none`) do not implement array submission, so any unmet condition falls back to the per-file submission path unchanged; for the SSH executor the capability exists only with `scheduler: slurm` (its `run_array` attribute is `None` otherwise).
56
+
57
+ The mechanism mirrors the per-file path. `run_array` writes a tab-separated manifest (`logs/<batch_id>.array-manifest.tsv`) with one line per task — index, run_id, stdout/stderr/exitcode/probe paths, and the rendered command as the last field — and renders one array script (`render_slurm_array_script`) that dispatches line N to array task N via `sed -n "${SLURM_ARRAY_TASK_ID}p"`. The SBATCH header, `setup_commands`, the `cd` guard, the per-task environment probe, and the self-written exit-code file are shared with the single-job script; `slurm.array_concurrency: N` becomes the `%N` throttle on `--array=1-M%N`. Each task keeps its own `logs/<run_id>.*` files, so per-file provenance granularity is unchanged: every file still gets its own `workflow_runs` row, with `scheduler_job_id` recorded as `<array_id>_<task_index>` and the array id kept in the run details as `array_job_id`. Accounting is queried per task (`sacct -j <array_id>_<index>`), so the sacct signal-folding semantics (an OOM kill's `0:9` becomes 137) apply per task exactly as for single jobs. Through the SSH executor with a remote Slurm scheduler the same flow runs remotely: manifest, probe scripts, and array script are staged over SFTP, `sbatch`/`squeue` run on the remote host, and per-task logs, exit codes, and sacct accounting are pulled back.
58
+
59
+ Array participation is a submission strategy, not an environment or parameter difference, so it never enters the parameter fingerprint or cache identity: a recipe shares the same completed cache with array submission on or off, and cache hits work identically either way. Because candidate order is deterministic and the manifest carries each task's rendered command, the task-to-file mapping is auditable and immune to resume/`--limit` drift.
60
+
61
+ Interrupt bookkeeping contract: on KeyboardInterrupt the whole array is cancelled once with `scancel` and the exception propagates; the caller distinguishes finished tasks by which per-task `<run_id>.exitcode` files exist (the remote path pulls back any exit-code files that already exist before re-raising, so the same check works against local paths). Tasks without an exit-code file are marked `interrupted` — they never pollute the completed cache — and their partial outputs are removed under the usual per-file semantics (`--keep-partial` keeps them), so re-running the same command resumes exactly the unfinished files.
52
62
 
53
63
  For day-to-day use, see the [how-to guides](../guides/index.md); for the full contract of fields, placeholders, artifacts, database identity, caching, and parsers, see the [recipe configuration reference](../reference/recipe-overview.md).
@@ -0,0 +1,71 @@
1
+ # Defect tracking
2
+
3
+ The current defect tracking of Operon records the confirmed defects in
4
+ a machine-readable registry: `defects.yml` at the repository root,
5
+ instead of external issue tracker. The registry is the single source
6
+ of truth for defect reports and their disposition; [Resolved issues](../reference/resolved-issues.md)
7
+ is the user-facing summary and keeps the historical K-series numbers
8
+ alongside the ODR series.
9
+
10
+ > K-series numbers are archived only in [Resolved Issues](../reference/resolved-issues.md).
11
+ > The registry will not be updated with, or supplemented by K-series numbers.
12
+
13
+ ## Registry
14
+
15
+ Every confirmed defect gets one record under `defects:` with a sequential
16
+ `ODR-XXXX` id. Records load from `defects.yml` plus any `defects/*.yml`
17
+ shards (sorted by name), so an overgrown registry can be split without
18
+ changing tooling.
19
+
20
+ Record fields:
21
+
22
+ | Field | Content |
23
+ |---|---|
24
+ | `id` | `ODR-XXXX`, sequential, unique (validated) |
25
+ | `title` | one-line summary |
26
+ | `reported` | ISO date the defect was confirmed |
27
+ | `introduced_in` | version or commit that introduced the defect, or `null` if undetermined |
28
+ | `affected` | affected versions and the preconditions for triggering |
29
+ | `severity` | `low` / `medium` / `high` / `critical` |
30
+ | `component` | primary module or subsystem |
31
+ | `status` | `open` → `confirmed` → `fixed` → `verified`; `wontfix` / `duplicate` are terminal |
32
+ | `reproduction` | observed behavior and how it was reproduced |
33
+ | `disposition` | what was done (or why no action) |
34
+ | `fix_commit` | 40-hex commit id once the fix lands; `null` while pending (format-validated only) |
35
+ | `fixed_in` | release version once shipped; `null` until then |
36
+ | `regression_tests` | `path::test` list; mandatory for `fixed`/`verified` |
37
+
38
+ Use `scripts/defects.sh` to append and query records:
39
+
40
+ ```bash
41
+ scripts/defects.sh list [--status open] [--component tools]
42
+ scripts/defects.sh show ODR-0001
43
+ scripts/defects.sh add --title "..." --severity medium --component tools \
44
+ --reproduction "..."
45
+ ```
46
+
47
+ The shell script is a small launcher for `scripts/defects.py`; it selects the
48
+ project virtual environment when available. `list` uses a table on wide
49
+ terminals and a compact per-record layout on narrow terminals. `show` formats
50
+ long record fields as wrapped sections. Colors are enabled only for an
51
+ interactive terminal and can be disabled with `NO_COLOR`.
52
+
53
+ `add` allocates the next id and appends a `status: open` record. It keeps the
54
+ existing registry schema and YAML storage format unchanged.
55
+
56
+ ## Rules
57
+
58
+ 1. **Register first.** When an audit or investigation confirms a defect,
59
+ append its record to `defects.yml` before the fix lands.
60
+ 2. **One commit per defect.** The fix and its regression tests ride in the
61
+ same commit; fill in `fix_commit` when committing.
62
+ 3. **Close the test loop.** Every regression test for a defect carries
63
+ `@pytest.mark.bug("ODR-XXXX")` (registered in `pyproject.toml`), and
64
+ every `fixed`/`verified` record lists at least one such test.
65
+ `tests/unit/test_defect_registry.py` validates the registry schema and
66
+ both directions of the marker closure, so a missing record, a dangling
67
+ marker, or a `fixed` record without a regression test fails the suite.
68
+ `python -m pytest -m bug` runs exactly the defect regressions.
69
+
70
+ `verified` additionally requires `fix_commit` and `fixed_in`, and is set
71
+ when the fix ships in a release.
@@ -2,7 +2,8 @@
2
2
 
3
3
  ## Setup and test runs
4
4
 
5
- Install the repository checkout with the `dev` extra as described in [Installation](../getting-started/installation.md#install-from-the-repository); it adds pytest, Cython, and Sphinx. Then:
5
+ Install the repository checkout with the `dev` extra as described in [Installation](../getting-started/installation.md#install-from-the-repository); it adds pytest, Cython, and
6
+ Sphinx. Then:
6
7
 
7
8
  ```bash
8
9
  python -m pytest
@@ -16,36 +17,44 @@ python -m pytest tests/regression tests/compatibility
16
17
  sphinx-build -W --keep-going -b html docs docs/_build/html
17
18
  ```
18
19
 
19
- The pytest suite is organized into four categories — `unit`, `integration`, `regression`, `compatibility` — covering: Python 3.10 syntax and runtime gates, schema validation and controlled vocabularies, metadata round-trips and transaction rollback, stable IDs, default copy isolation, query read-only constraints, file-aware QC identity, profile/decision history, gzip FASTA recognition, assembly/annotation QC, rule decisions, idempotent ingest and conflict protection, checksum-tamper detection, the demo end-to-end pipeline and release verification, the NCBI Datasets adapter, wrapped BLAST/HMMER/BUSCO execution, directory artifacts, JSON summaries, conda run prefix parsing, cache hits/forced re-runs, result write-back, and input-tamper rejection.
20
- The taxonomy coverage integration tests additionally cover taxonomy source-package identity conflicts, profile type/content conflicts, exclusion rules, secondary TaxIDs, denominator/report idempotence, and that active metadata modifications do not affect the release-frozen scope.
20
+ The pytest suite is organized into four categories — `unit`, `integration`, `regression`, `compatibility` — covering:
21
+ Python 3.10 syntax and runtime gates, schema validation and controlled vocabularies, metadata round-trips and
22
+ transaction rollback, stable IDs, default copy isolation, query read-only constraints, file-aware QC identity,
23
+ profile/decision history, gzip FASTA recognition, assembly/annotation QC, rule decisions, idempotent ingest and conflict
24
+ protection, checksum-tamper detection, the demo end-to-end pipeline and release verification, the NCBI Datasets adapter,
25
+ wrapped BLAST/HMMER/BUSCO execution, directory artifacts, JSON summaries, conda run prefix parsing, cache hits/forced
26
+ re-runs, result write-back, and input-tamper rejection.
27
+ The taxonomy coverage integration tests additionally cover taxonomy source-package identity conflicts, profile
28
+ type/content conflicts, exclusion rules, secondary TaxIDs, denominator/report idempotence, and that active metadata
29
+ modifications do not affect the release-frozen scope.
30
+
31
+ The project uses `pytest-xdist` to parallel testing. Avoid sharing state between tests to prevent unexpected or random
32
+ test results.
21
33
 
22
34
  ## Fast local verification
23
35
 
24
- The full suite takes about seven minutes serially; the loop below aims to run it at most
25
- once per change.
36
+ The full suite takes about seven minutes serially; the loop below aims to run it at most once per change.
26
37
 
27
38
  1. **Re-run only what failed.** `python -m pytest --lf -q --no-cov` replays pytest's
28
39
  last-failed cache before anything else.
29
40
  2. **Stop at the first failure** while iterating: `python -m pytest tests/unit -x -q --no-cov`,
30
41
  with the file or test you actually touched rather than a whole category.
31
- 3. **Parallelize.** `python -m pytest -n 4 --dist loadfile` (pytest-xdist) runs the suite in
32
- about a quarter of the time — roughly 7 minutes to under 2 on a 24-core workstation — and
33
- `--dist loadfile` keeps each test file on one worker, which the Textual UI tests need.
34
- It ships with the `test` and `dev` extras (`python -m pip install -e '.[dev]'`). Coverage is the expensive part:
35
- iterate with `--no-cov` and measure once, at the end.
36
- 4. **Cross-version matrix in one pass.** `scripts/setup-test-matrix.sh` creates uv-managed
37
- CPython 3.10-3.13 interpreters plus one venv per version inside `.matrix/` (3.14 comes
38
- from the project `.venv`), and `scripts/run-test-matrix.sh` runs all five concurrently:
42
+ 3. **Parallel execution.** On a 24-core workstation, running `python -m pytest -n 24 --dist loadfile`
43
+ (with `pytest-xdist`) can reduce the execution time to 30–40 seconds. `--dist loadfile` ensures that each test file
44
+ is assigned to only one worker, which is required for Textual UI testing.
45
+ Coverages are the most resource-intensive: use `--no-cov` during iterations and run the test only once at the end.
46
+ 4. **Cross-version matrix in one pass.** `scripts/setup-test-matrix.sh` creates uv-managed CPython 3.10-3.15
47
+ interpreters plus one venv per version inside `.matrix/`. `scripts/run-test-matrix.sh` runs all six concurrently:
39
48
 
40
49
  ```bash
41
50
  scripts/setup-test-matrix.sh # once per machine
42
- scripts/run-test-matrix.sh # whole suite, five versions, ~3 minutes
51
+ scripts/run-test-matrix.sh # whole suite, six versions, 2~3 minutes
43
52
  scripts/run-test-matrix.sh tests/unit -x # extra pytest arguments are forwarded
44
- MATRIX_JOBS=8 scripts/run-test-matrix.sh # workers per version
53
+ MATRIX_JOBS=4 scripts/run-test-matrix.sh # workers per version
45
54
  ```
46
55
 
47
56
  Each version logs to `.matrix/logs/<version>.log`, so a failure there can be re-run
48
- directly with that interpreter. The scripts work on Linux and macOS; `.matrix/` is
57
+ directly with that interpreter. The scripts work on Linux and macOS. `.matrix/` is
49
58
  git-ignored.
50
59
  5. **Platform-specific code cannot be verified by one matrix job alone.** Linux and macOS
51
60
  differ in the system utilities the environment probe calls and in `pathlib`/`resource`
@@ -53,14 +62,12 @@ once per change.
53
62
  should carry a test that reproduces the other platform's condition on Linux — for
54
63
  example an empty `PATH` without GNU `timeout` — rather than depending on the macOS job
55
64
  to notice.
56
- 6. **CI runs the matrix in parallel.** The strict documentation build is its own small
57
- job, and the ten-job matrix (two workers per job, so a four-core runner is not
58
- oversubscribed) starts immediately; use the local ladder above when you want a faster
59
- verdict than the matrix can give.
60
65
 
61
66
  ## Special Note For Codex/ChatGPT
62
67
 
63
- Due to specific limitations of the sandbox environment, executing certain TUI test code may cause the system to freeze; this often occurs on Codex/ChatGPT. The specific reason is that when this test code runs in the sandbox, it may results a Textual/asyncio cleanup block, and reports "FAIL" due to timeout.
68
+ Due to specific limitations of the sandbox environment, executing certain TUI test code may cause the system to freeze;
69
+ this often occurs on Codex/ChatGPT. The specific reason is that when this test code runs in the sandbox, it may result
70
+ a Textual/asyncio cleanup block, and reports "FAIL" due to timeout.
64
71
 
65
72
  If you are Codex/ChatGPT, please execute TUI-related test code OUTSIDE the sandbox.
66
73
 
@@ -68,7 +75,10 @@ This information has also been updated in AGENTS.md.
68
75
 
69
76
  ## Coverage gate
70
77
 
71
- `python -m pytest` always measures coverage (`--cov=operon --cov-branch`, configured in `pyproject.toml`) and fails when the reported total drops below the `fail_under` threshold. The reported total is the combined figure `(covered lines + covered branches) / (valid lines + valid branches)`, so a change that adds only lines without their branches lowers it. Branch coverage must additionally stay at or above 90 % of all valid branches.
78
+ `python -m pytest` always measures coverage (`--cov=operon --cov-branch`, configured in `pyproject.toml`) and fails when
79
+ the reported total drops below the `fail_under` threshold. The reported total is the combined figure
80
+ `(covered lines + covered branches) / (valid lines + valid branches)`, so a change that adds only lines without their
81
+ branches lowers it. Branch coverage must additionally stay at or above 95% of all valid branches.
72
82
 
73
83
  Read the gaps with:
74
84
 
@@ -4,6 +4,7 @@
4
4
  - [Development and Testing](development-testing.md)
5
5
  - [PyPI Release](pypi-release.md)
6
6
  - [Documentation builds and Read the Docs deployment](documentation-deployment.md)
7
+ - [Defect tracking](defect-tracking.md)
7
8
 
8
9
  ```{toctree}
9
10
  :hidden:
@@ -12,4 +13,5 @@ repository-guide
12
13
  development-testing
13
14
  pypi-release
14
15
  documentation-deployment
16
+ defect-tracking
15
17
  ```
@@ -66,7 +66,7 @@ Roles are freely named by your workflow. Every product is materialized under `an
66
66
  Subfamily assignment is the point where the pipeline's shape becomes data-dependent: only after the external assign-subfamilies step reads the adopted framework tree is the number of subfamilies known. Do not hand-write one adopt manifest entry per subfamily. Have the external step emit a clean two-column assignment TSV (`unit`, `seqid` — project-specific filtering such as dropping unresolved sequences happens there), adopt that TSV as shown in step 3, then admit the units with `operon fanout`:
67
67
 
68
68
  ```bash
69
- # Inspect the plan first: no files, no run row.
69
+ # Inspect the plan first: full preflight runs, but no files and no run row.
70
70
  operon fanout --assignments-file FIL_000022 \
71
71
  --source-file FIL_000012 \
72
72
  --entity-type organism --entity-id ORG_000001 \
@@ -40,7 +40,10 @@ TUI Config screen (`operon tui`, key `6`, QC Profiles tab): a structured form
40
40
  (description, `applies_to` checkboxes, rule rows) that validates the composed
41
41
  document, bumps the `version`, writes the file, and records the same
42
42
  content-addressed snapshot `operon evaluate` records — with snapshot history
43
- and restore-as-new-version built in.
43
+ and restore-as-new-version built in. When a file has been deleted or replaced
44
+ with an older version, the next changed save continues above the highest
45
+ recorded version for that name. See [the TUI reference](../reference/cli-tui.md)
46
+ for the confirmation and recovery behavior.
44
47
 
45
48
  Run the profile:
46
49
 
@@ -205,11 +208,11 @@ Grammar:
205
208
 
206
209
  - `applies_to` is a mapping with `entity_type` and `file_role` (unlike the list form of `kind: qc` profiles); it selects the target manifest files. Superseded and effectively retired entities are excluded.
207
210
  - `sources` names the hit sources the rules can reference. Each source declares:
208
- - `analysis`: the analysis name whose `analysis_alignments` rows count; only rows of the latest `completed` job per target file contribute.
211
+ - `analysis`: the analysis name whose `analysis_alignments` rows count; only rows of the latest `completed` job per target file contribute. Other completed jobs for the same analysis+file pair are ignored, and a non-zero count is surfaced as `ignored_completed_jobs` in the run output and details.
209
212
  - `filter`: which rows count as hits at all (a list of conditions, AND-ed; empty means every row).
210
213
  - `best_by`: an ordered best-hit ranking; the first surviving row per seqid is the best hit the rules see. Each entry is a `field` with `direction: asc|desc` (default `asc`), or a `rank` map from value to rank (unmapped values sort after all mapped ones; an entry-level `default` sets their rank explicitly). Without `best_by`, rows order by `hit_rank` ascending.
211
214
  - `rules` are evaluated in order and the first match wins. A rule carries a `label` plus exactly one of: a non-empty `when` list of conditions checked against the source's best hit, `absent: true` (the source has no hit row at all for the sequence — tier U above), or `default: true` (catch-all; takes no `source`/`when`/`absent`). A sequence matched by nothing stays unlabeled.
212
215
  - Conditions — the grammar is shared between `filter` and `when` — are mappings with a `field` and an `operator`: `>=`, `<=`, `>`, `<`, `==`, `!=` (numeric, with string fallback for equality), `in`/`not_in` (with a `values` list), `between` (with `min`/`max`), `exists`, and `like` (case-insensitive SQL LIKE pattern with `%` and `_` wildcards). A condition may instead be `any: [...]` (a group whose conditions are OR-ed) or `not: {...}` (negation of a single condition); the top-level list is always AND-ed.
213
- - Fields resolve against the alignment columns (`subject_id`, `hit_rank`, `query_start`, `query_end`, `evalue`, `bitscore`, `percent_identity`, …), then the derived `span` field (`query_end - query_start + 1`) and `seqid` (the query id up to the first whitespace), and finally the keys of the hit row's `extra_json` — parser-specific fields such as `hit_type`, `incomplete`, `short_name`, or `i_evalue`. A missing field makes the test False for every operator (including `!=`, `not_in`, and `exists`), so only wrapping the condition in `not: {...}` makes it hold — the negation of that False is True.
216
+ - Fields resolve against the alignment columns (`subject_id`, `hit_rank`, `query_start`, `query_end`, `evalue`, `bitscore`, `percent_identity`, …), then the derived `span` field (`query_end - query_start + 1`) and `seqid` (the query id up to the first whitespace), and finally the keys of the hit row's `extra_json` — parser-specific fields such as `hit_type`, `incomplete`, `short_name`, or `i_evalue`. A missing field never satisfies a condition in any form: the test is False for every operator (including `!=`, `not_in`, and `exists`), a `not: {...}` negation of a missing-field condition is still False, and a missing-field disjunct inside `any: [...]` counts as unsatisfied. `between` requires numeric operands and raises a validation error naming the field and value otherwise; `in`/`not_in` compare values as strings.
214
217
 
215
218
  Re-running with the same profile content and the same inputs is a no-op; editing the profile re-decides the affected sequences and audits every label change in `changes`.