OperonDBS 0.8.4__tar.gz → 0.9.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 (329) hide show
  1. {operondbs-0.8.4 → operondbs-0.9.0}/.github/workflows/publish.yml +6 -2
  2. {operondbs-0.8.4 → operondbs-0.9.0}/.github/workflows/test.yml +4 -1
  3. {operondbs-0.8.4 → operondbs-0.9.0}/AGENTS.md +26 -13
  4. operondbs-0.9.0/LICENSE +22 -0
  5. {operondbs-0.8.4 → operondbs-0.9.0}/OperonDBS.egg-info/PKG-INFO +2 -2
  6. {operondbs-0.8.4 → operondbs-0.9.0}/OperonDBS.egg-info/SOURCES.txt +21 -0
  7. {operondbs-0.8.4 → operondbs-0.9.0}/PKG-INFO +2 -2
  8. {operondbs-0.8.4 → operondbs-0.9.0}/docs/conf_common.py +1 -1
  9. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/overview.md +2 -0
  10. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/metadata-import.md +4 -0
  11. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/remote-execution.md +53 -1
  12. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/remote-first.md +9 -0
  13. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/taxonomy-coverage.md +10 -1
  14. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/behaviors-and-limitations.md +2 -2
  15. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-analysis.md +1 -1
  16. operondbs-0.9.0/docs/en/reference/cli-config.md +152 -0
  17. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-decisions-reports.md +3 -2
  18. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-files-qc.md +1 -0
  19. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-project-metadata.md +6 -1
  20. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-tui.md +95 -10
  21. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/index.md +2 -0
  22. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/recipe-fields.md +4 -1
  23. operondbs-0.9.0/docs/en/reference/resolved-issues.md +95 -0
  24. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/overview.md +2 -0
  25. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/metadata-import.md +3 -0
  26. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/remote-execution.md +90 -5
  27. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/remote-first.md +17 -0
  28. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/taxonomy-coverage.md +10 -1
  29. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/behaviors-and-limitations.md +2 -2
  30. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-analysis.md +5 -1
  31. operondbs-0.9.0/docs/zh/reference/cli-config.md +138 -0
  32. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-decisions-reports.md +7 -2
  33. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-files-qc.md +1 -0
  34. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-project-metadata.md +8 -1
  35. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-tui.md +78 -7
  36. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/index.md +2 -0
  37. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/recipe-fields.md +9 -1
  38. operondbs-0.9.0/docs/zh/reference/resolved-issues.md +87 -0
  39. {operondbs-0.8.4 → operondbs-0.9.0}/operon/adapters/ncbi_datasets.py +809 -473
  40. {operondbs-0.8.4 → operondbs-0.9.0}/operon/cli.py +248 -282
  41. operondbs-0.9.0/operon/config.py +619 -0
  42. {operondbs-0.8.4 → operondbs-0.9.0}/operon/entity_view.py +212 -142
  43. {operondbs-0.8.4 → operondbs-0.9.0}/operon/environment.py +2 -2
  44. {operondbs-0.8.4 → operondbs-0.9.0}/operon/environment_capture.py +109 -58
  45. {operondbs-0.8.4 → operondbs-0.9.0}/operon/errors.py +4 -0
  46. {operondbs-0.8.4 → operondbs-0.9.0}/operon/execution.py +3 -3
  47. {operondbs-0.8.4 → operondbs-0.9.0}/operon/files.py +5 -5
  48. {operondbs-0.8.4 → operondbs-0.9.0}/operon/import_wizard.py +3 -4
  49. {operondbs-0.8.4 → operondbs-0.9.0}/operon/ncbi_reconcile.py +2 -2
  50. operondbs-0.9.0/operon/pipeline.py +201 -0
  51. {operondbs-0.8.4 → operondbs-0.9.0}/operon/qc/__init__.py +1 -1
  52. {operondbs-0.8.4 → operondbs-0.9.0}/operon/qc/_alignment.pyx +3 -1
  53. {operondbs-0.8.4 → operondbs-0.9.0}/operon/qc/alignment.py +5 -2
  54. operondbs-0.9.0/operon/qc/imports.py +276 -0
  55. {operondbs-0.8.4 → operondbs-0.9.0}/operon/remotes.py +9 -2
  56. {operondbs-0.8.4 → operondbs-0.9.0}/operon/reports.py +1 -1
  57. {operondbs-0.8.4 → operondbs-0.9.0}/operon/schema.py +166 -6
  58. operondbs-0.9.0/operon/secrets.py +327 -0
  59. {operondbs-0.8.4 → operondbs-0.9.0}/operon/sequence_tools.py +190 -111
  60. {operondbs-0.8.4 → operondbs-0.9.0}/operon/taxonomy.py +49 -15
  61. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tools.py +511 -280
  62. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/actions.py +457 -19
  63. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/app.py +27 -3
  64. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/app.tcss +77 -0
  65. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/data.py +5 -0
  66. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/parity.py +158 -18
  67. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/analyze.py +5 -1
  68. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/common.py +225 -32
  69. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/config.py +374 -18
  70. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/config_classification.py +58 -15
  71. operondbs-0.9.0/operon/tui/screens/config_coverage.py +143 -0
  72. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/coverage.py +22 -4
  73. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/decisions.py +2 -3
  74. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/derived_ops.py +6 -2
  75. operondbs-0.9.0/operon/tui/screens/entities.py +687 -0
  76. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/environments.py +2 -2
  77. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/files.py +85 -4
  78. operondbs-0.9.0/operon/tui/screens/files_ops.py +627 -0
  79. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/hits.py +2 -2
  80. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/home.py +24 -1
  81. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/import_wizard.py +3 -3
  82. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/labels.py +1 -1
  83. operondbs-0.9.0/operon/tui/screens/ncbi_datasets.py +405 -0
  84. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/publish.py +12 -8
  85. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/run_external.py +4 -0
  86. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/runs.py +2 -2
  87. operondbs-0.9.0/operon/tui/screens/table_import.py +326 -0
  88. operondbs-0.9.0/operon/tui/screens/taxonomy.py +269 -0
  89. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/splash.py +6 -1
  90. {operondbs-0.8.4 → operondbs-0.9.0}/operon/utils.py +15 -0
  91. {operondbs-0.8.4 → operondbs-0.9.0}/operon/workflow.py +3 -3
  92. {operondbs-0.8.4 → operondbs-0.9.0}/pyproject.toml +2 -2
  93. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_alignment_qc.py +45 -0
  94. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_cli_edges.py +18 -13
  95. operondbs-0.9.0/tests/unit/test_config_cli.py +290 -0
  96. operondbs-0.9.0/tests/unit/test_env_audit.py +73 -0
  97. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_environment_capture.py +23 -4
  98. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_execution.py +27 -5
  99. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_lifecycle.py +38 -2
  100. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_ncbi_coverage_gaps.py +68 -0
  101. operondbs-0.9.0/tests/unit/test_pipeline.py +213 -0
  102. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_remotes.py +3 -1
  103. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_rules_schema_edges.py +67 -0
  104. operondbs-0.9.0/tests/unit/test_secrets.py +369 -0
  105. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_timetree_snapshots.py +5 -1
  106. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tools_execution_more.py +44 -0
  107. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui.py +351 -18
  108. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_analyze.py +100 -5
  109. operondbs-0.9.0/tests/unit/test_tui_button_dispatch.py +75 -0
  110. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_cli_parity.py +486 -4
  111. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_config.py +1003 -23
  112. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_derived_ops.py +37 -9
  113. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_flows_more.py +43 -6
  114. operondbs-0.9.0/tests/unit/test_tui_import_qc.py +306 -0
  115. operondbs-0.9.0/tests/unit/test_tui_ncbi_datasets.py +607 -0
  116. operondbs-0.9.0/tests/unit/test_tui_pipeline.py +311 -0
  117. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_publish.py +260 -2
  118. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_screens_more.py +236 -16
  119. operondbs-0.9.0/tests/unit/test_tui_standardize.py +265 -0
  120. operondbs-0.9.0/tests/unit/test_tui_table_import.py +782 -0
  121. operondbs-0.9.0/tests/unit/test_tui_taxonomy.py +967 -0
  122. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_writes.py +694 -17
  123. operondbs-0.9.0/tests/unit/test_user_config.py +328 -0
  124. operondbs-0.8.4/LICENSE +0 -661
  125. operondbs-0.8.4/docs/en/reference/resolved-issues.md +0 -64
  126. operondbs-0.8.4/docs/zh/reference/resolved-issues.md +0 -56
  127. operondbs-0.8.4/operon/config.py +0 -238
  128. operondbs-0.8.4/operon/tui/screens/entities.py +0 -349
  129. operondbs-0.8.4/operon/tui/screens/files_ops.py +0 -278
  130. {operondbs-0.8.4 → operondbs-0.9.0}/.gitignore +0 -0
  131. {operondbs-0.8.4 → operondbs-0.9.0}/MANIFEST.in +0 -0
  132. {operondbs-0.8.4 → operondbs-0.9.0}/OperonDBS.egg-info/dependency_links.txt +0 -0
  133. {operondbs-0.8.4 → operondbs-0.9.0}/OperonDBS.egg-info/entry_points.txt +0 -0
  134. {operondbs-0.8.4 → operondbs-0.9.0}/OperonDBS.egg-info/requires.txt +0 -0
  135. {operondbs-0.8.4 → operondbs-0.9.0}/OperonDBS.egg-info/top_level.txt +0 -0
  136. {operondbs-0.8.4 → operondbs-0.9.0}/README.md +0 -0
  137. {operondbs-0.8.4 → operondbs-0.9.0}/README_ZH.md +0 -0
  138. {operondbs-0.8.4 → operondbs-0.9.0}/benchmarks/qc_representative_entities.tsv +0 -0
  139. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/.readthedocs.yaml +0 -0
  140. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/extensibility.md +0 -0
  141. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/external-analysis.md +0 -0
  142. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/files-and-storage.md +0 -0
  143. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/index.md +0 -0
  144. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/metadata-and-data-model.md +0 -0
  145. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/qc-and-rules.md +0 -0
  146. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/release-lifecycle.md +0 -0
  147. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/architecture/taxonomy-coverage.md +0 -0
  148. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/conf.py +0 -0
  149. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/contributor/defect-tracking.md +0 -0
  150. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/contributor/development-testing.md +0 -0
  151. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/contributor/documentation-deployment.md +0 -0
  152. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/contributor/index.md +0 -0
  153. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/contributor/pypi-release.md +0 -0
  154. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/contributor/repository-guide.md +0 -0
  155. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/getting-started/daily-workflow.md +0 -0
  156. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/getting-started/first-project.md +0 -0
  157. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/getting-started/index.md +0 -0
  158. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/getting-started/installation.md +0 -0
  159. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/getting-started/quickstart.md +0 -0
  160. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/backup-migration.md +0 -0
  161. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/curation-lifecycle.md +0 -0
  162. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/external-analysis.md +0 -0
  163. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/file-archiving.md +0 -0
  164. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/index.md +0 -0
  165. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/ncbi-datasets.md +0 -0
  166. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/phylogenetics-workflow.md +0 -0
  167. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/qc-profiles.md +0 -0
  168. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/remote-storage.md +0 -0
  169. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/guides/troubleshooting.md +0 -0
  170. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/index.md +0 -0
  171. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/operations/database-compatibility.md +0 -0
  172. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/operations/index.md +0 -0
  173. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/operations/ncbi-recovery-migration.md +0 -0
  174. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/operations/qc-performance.md +0 -0
  175. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/overview.md +0 -0
  176. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-remote.md +0 -0
  177. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-taxonomy-lifecycle-admin.md +0 -0
  178. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/cli-workflow.md +0 -0
  179. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/data-model.md +0 -0
  180. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/recipe-overview.md +0 -0
  181. {operondbs-0.8.4 → operondbs-0.9.0}/docs/en/reference/recipe-parsers-examples.md +0 -0
  182. {operondbs-0.8.4 → operondbs-0.9.0}/docs/locales/README.md +0 -0
  183. {operondbs-0.8.4 → operondbs-0.9.0}/docs/requirements.txt +0 -0
  184. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/.readthedocs.yaml +0 -0
  185. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/extensibility.md +0 -0
  186. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/external-analysis.md +0 -0
  187. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/files-and-storage.md +0 -0
  188. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/index.md +0 -0
  189. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/metadata-and-data-model.md +0 -0
  190. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/qc-and-rules.md +0 -0
  191. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/release-lifecycle.md +0 -0
  192. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/architecture/taxonomy-coverage.md +0 -0
  193. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/conf.py +0 -0
  194. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/contributor/defect-tracking.md +0 -0
  195. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/contributor/development-testing.md +0 -0
  196. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/contributor/documentation-deployment.md +0 -0
  197. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/contributor/index.md +0 -0
  198. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/contributor/pypi-release.md +0 -0
  199. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/contributor/repository-guide.md +0 -0
  200. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/getting-started/daily-workflow.md +0 -0
  201. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/getting-started/first-project.md +0 -0
  202. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/getting-started/index.md +0 -0
  203. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/getting-started/installation.md +0 -0
  204. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/getting-started/quickstart.md +0 -0
  205. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/backup-migration.md +0 -0
  206. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/curation-lifecycle.md +0 -0
  207. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/external-analysis.md +0 -0
  208. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/file-archiving.md +0 -0
  209. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/index.md +0 -0
  210. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/ncbi-datasets.md +0 -0
  211. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/phylogenetics-workflow.md +0 -0
  212. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/qc-profiles.md +0 -0
  213. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/remote-storage.md +0 -0
  214. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/guides/troubleshooting.md +0 -0
  215. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/index.md +0 -0
  216. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/operations/database-compatibility.md +0 -0
  217. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/operations/index.md +0 -0
  218. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/operations/ncbi-recovery-migration.md +0 -0
  219. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/operations/qc-performance.md +0 -0
  220. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/overview.md +0 -0
  221. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-remote.md +0 -0
  222. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-taxonomy-lifecycle-admin.md +0 -0
  223. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/cli-workflow.md +0 -0
  224. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/data-model.md +0 -0
  225. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/recipe-overview.md +0 -0
  226. {operondbs-0.8.4 → operondbs-0.9.0}/docs/zh/reference/recipe-parsers-examples.md +0 -0
  227. {operondbs-0.8.4 → operondbs-0.9.0}/operon/__init__.py +0 -0
  228. {operondbs-0.8.4 → operondbs-0.9.0}/operon/__main__.py +0 -0
  229. {operondbs-0.8.4 → operondbs-0.9.0}/operon/adapters/__init__.py +0 -0
  230. {operondbs-0.8.4 → operondbs-0.9.0}/operon/adapters/timetree.py +0 -0
  231. {operondbs-0.8.4 → operondbs-0.9.0}/operon/backup.py +0 -0
  232. {operondbs-0.8.4 → operondbs-0.9.0}/operon/classify.py +0 -0
  233. {operondbs-0.8.4 → operondbs-0.9.0}/operon/coverage.py +0 -0
  234. {operondbs-0.8.4 → operondbs-0.9.0}/operon/database.py +0 -0
  235. {operondbs-0.8.4 → operondbs-0.9.0}/operon/demo.py +0 -0
  236. {operondbs-0.8.4 → operondbs-0.9.0}/operon/export.py +0 -0
  237. {operondbs-0.8.4 → operondbs-0.9.0}/operon/fanout.py +0 -0
  238. {operondbs-0.8.4 → operondbs-0.9.0}/operon/lifecycle.py +0 -0
  239. {operondbs-0.8.4 → operondbs-0.9.0}/operon/lineage.py +0 -0
  240. {operondbs-0.8.4 → operondbs-0.9.0}/operon/metadata_files.py +0 -0
  241. {operondbs-0.8.4 → operondbs-0.9.0}/operon/profiles.py +0 -0
  242. {operondbs-0.8.4 → operondbs-0.9.0}/operon/qc/_parsers.pyx +0 -0
  243. {operondbs-0.8.4 → operondbs-0.9.0}/operon/qc/measure.py +0 -0
  244. {operondbs-0.8.4 → operondbs-0.9.0}/operon/qc/parsers.py +0 -0
  245. {operondbs-0.8.4 → operondbs-0.9.0}/operon/release.py +0 -0
  246. {operondbs-0.8.4 → operondbs-0.9.0}/operon/rules.py +0 -0
  247. {operondbs-0.8.4 → operondbs-0.9.0}/operon/shutdown.py +0 -0
  248. {operondbs-0.8.4 → operondbs-0.9.0}/operon/sql.py +0 -0
  249. {operondbs-0.8.4 → operondbs-0.9.0}/operon/table_import.py +0 -0
  250. {operondbs-0.8.4 → operondbs-0.9.0}/operon/timetree.py +0 -0
  251. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/__init__.py +0 -0
  252. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/assets/splash.png +0 -0
  253. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/assets/splash.rgb.z +0 -0
  254. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/__init__.py +0 -0
  255. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/screens/classify.py +0 -0
  256. {operondbs-0.8.4 → operondbs-0.9.0}/operon/tui/splash_terminal.py +0 -0
  257. {operondbs-0.8.4 → operondbs-0.9.0}/setup.cfg +0 -0
  258. {operondbs-0.8.4 → operondbs-0.9.0}/setup.py +0 -0
  259. {operondbs-0.8.4 → operondbs-0.9.0}/tests/__init__.py +0 -0
  260. {operondbs-0.8.4 → operondbs-0.9.0}/tests/compatibility/__init__.py +0 -0
  261. {operondbs-0.8.4 → operondbs-0.9.0}/tests/compatibility/test_python_support.py +0 -0
  262. {operondbs-0.8.4 → operondbs-0.9.0}/tests/helpers.py +0 -0
  263. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/__init__.py +0 -0
  264. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_analysis_array.py +0 -0
  265. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_analysis_resume.py +0 -0
  266. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_analysis_shutdown.py +0 -0
  267. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_analysis_tools.py +0 -0
  268. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_classify_sequences.py +0 -0
  269. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_execution_backends.py +0 -0
  270. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_fanout_analyze.py +0 -0
  271. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_lineage_cascade.py +0 -0
  272. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_ncbi_datasets_adapter.py +0 -0
  273. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_pipeline_and_release.py +0 -0
  274. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_python_packaging.py +0 -0
  275. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_qc_remote_measure.py +0 -0
  276. {operondbs-0.8.4 → operondbs-0.9.0}/tests/integration/test_taxonomy_coverage.py +0 -0
  277. {operondbs-0.8.4 → operondbs-0.9.0}/tests/regression/__init__.py +0 -0
  278. {operondbs-0.8.4 → operondbs-0.9.0}/tests/regression/test_correctness.py +0 -0
  279. {operondbs-0.8.4 → operondbs-0.9.0}/tests/regression/test_cython_alignment_parity.py +0 -0
  280. {operondbs-0.8.4 → operondbs-0.9.0}/tests/regression/test_cython_parser_parity.py +0 -0
  281. {operondbs-0.8.4 → operondbs-0.9.0}/tests/regression/test_parser_semantics.py +0 -0
  282. {operondbs-0.8.4 → operondbs-0.9.0}/tests/regression/test_sql_identifier_security.py +0 -0
  283. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/__init__.py +0 -0
  284. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_classify.py +0 -0
  285. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_config_workflow_edges.py +0 -0
  286. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_core_coverage_more.py +0 -0
  287. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_coverage_edges.py +0 -0
  288. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_database_edges_more.py +0 -0
  289. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_defect_registry.py +0 -0
  290. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_defects_cli.py +0 -0
  291. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_docs_projects.py +0 -0
  292. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_docs_versions.py +0 -0
  293. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_environment.py +0 -0
  294. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_execution_edges.py +0 -0
  295. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_export.py +0 -0
  296. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_fanout.py +0 -0
  297. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_files_edges.py +0 -0
  298. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_follow.py +0 -0
  299. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_import_backup_show.py +0 -0
  300. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_import_wizard_edges.py +0 -0
  301. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_lineage.py +0 -0
  302. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_misc_coverage_more.py +0 -0
  303. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_ncbi_edge_cases.py +0 -0
  304. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_ncbi_reconcile_edges.py +0 -0
  305. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_parser_edge_paths.py +0 -0
  306. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_qc_and_rules.py +0 -0
  307. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_qc_edges_more.py +0 -0
  308. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_qc_measure.py +0 -0
  309. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_qc_remote_only.py +0 -0
  310. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_recipe_history.py +0 -0
  311. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_remote_taxonomy_more.py +0 -0
  312. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_remote_transport_edges.py +0 -0
  313. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_remotes_edges.py +0 -0
  314. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_schema_2_10.py +0 -0
  315. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_schema_2_11.py +0 -0
  316. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_schema_2_9.py +0 -0
  317. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_schema_and_metadata.py +0 -0
  318. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_sequence_tools.py +0 -0
  319. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_sequences.py +0 -0
  320. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_shutdown.py +0 -0
  321. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_support_edges.py +0 -0
  322. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_table_import_edges.py +0 -0
  323. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_taxonomy_edges.py +0 -0
  324. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_timetree.py +0 -0
  325. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tools_edges.py +0 -0
  326. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_m3_loop.py +0 -0
  327. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_tui_splash_terminal.py +0 -0
  328. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_views_release_reports_edges.py +0 -0
  329. {operondbs-0.8.4 → operondbs-0.9.0}/tests/unit/test_workflow_cli.py +0 -0
@@ -78,6 +78,8 @@ jobs:
78
78
  runs-on: ubuntu-latest
79
79
  steps:
80
80
  - uses: actions/checkout@v6
81
+ with:
82
+ persist-credentials: false
81
83
  - uses: actions/setup-python@v7
82
84
  with:
83
85
  python-version: "3.12"
@@ -133,11 +135,13 @@ jobs:
133
135
  name: pypi
134
136
  url: https://pypi.org/project/operondbs/
135
137
  permissions:
136
- id-token: write
138
+ # This is a standard OIDC Keyless Authorization Statements,
139
+ # NOT a hardcoding token
140
+ id-token: write # nosemgrep: security.hard-coded-tokens
137
141
  steps:
138
142
  - uses: actions/download-artifact@v8
139
143
  with:
140
144
  pattern: python-package-*
141
145
  path: dist/
142
146
  merge-multiple: true
143
- - uses: pypa/gh-action-pypi-publish@release/v1
147
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -6,6 +6,9 @@ on:
6
6
  - master
7
7
  pull_request:
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  # A newer push supersedes an in-flight run of the same ref.
10
13
  concurrency:
11
14
  group: ${{ github.workflow }}-${{ github.ref }}
@@ -49,6 +52,6 @@ jobs:
49
52
  python -m pip install --upgrade pip
50
53
  python -m pip install -e '.[test]'
51
54
  python -m pytest
52
- - uses: codacy/codacy-coverage-reporter-action@v1.3.0
55
+ - uses: codacy/codacy-coverage-reporter-action@89d6c85cfafaec52c72b6c5e8b2878d33104c699 # v1.3.0
53
56
  with:
54
57
  project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
@@ -26,12 +26,10 @@ principle-to-implementation mapping.
26
26
 
27
27
  Current version markers (must stay consistent across code and docs):
28
28
 
29
- - `operon` 0.8.4 (`pyproject.toml`)
29
+ - `operon` 0.9.0 (`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
 
33
- The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
34
-
35
33
  ## Repository layout
36
34
 
37
35
  - `operon/` — the Python package (CLI entry points: `operon/cli.py`,
@@ -56,7 +54,10 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
56
54
  `tests/regression/test_cython_parser_parity.py`). `measure.py` backs
57
55
  `operon qc-measure`, a project-independent measurement-only path whose
58
56
  JSON payload can be imported back through `operon import-qc` (the remote
59
- built-in QC workflow).
57
+ built-in QC workflow; `imports.py` is the shared core of that import —
58
+ payload/TSV detection, manifest validation, metric insertion, QC-state
59
+ recomputation and the run record — and `plan_qc_import` its write-free
60
+ preview).
60
61
  - `operon/execution.py` — execution backends for external commands:
61
62
  `local` subprocess, `slurm` (sbatch submit + squeue poll), and `ssh`
62
63
  (Paramiko; HPC head nodes and cloud VMs, optionally through remote
@@ -68,7 +69,9 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
68
69
  monitor, a Decisions screen, a Config screen, a Publish screen (nav key
69
70
  `7`; release builder + selective export builder with read-only previews),
70
71
  a Coverage screen (nav key `8`; taxonomy snapshots, reference sets,
71
- coverage report generation and `COV_*` report browsing), and the import
72
+ coverage report generation and `COV_*` report browsing, plus *Import
73
+ taxonomy…* / *Compile reference set…* write entries mirroring
74
+ `operon taxonomy import`/`operon taxonomy compile`), and the import
72
75
  dataset wizard (`operon/tui/screens/import_wizard.py`; Home button or
73
76
  global `i`, except on the Files screen where `i` stays ingest). Read
74
77
  access lives in
@@ -83,6 +86,11 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
83
86
  Confirm → background worker → notify + reload or inline error. Phase 3
84
87
  actions in the same module: `import_dataset` (commits wizard drafts
85
88
  through the shared single-transaction `import_wizard._commit`),
89
+ `ncbi_datasets` (backing the Home screen's NCBI Datasets import dialog:
90
+ a mandatory dry-run preflight — `dry_run` for offline inputs,
91
+ `plan_only` for accession-only requests — gates Confirm, and Cancel sets
92
+ a cooperative `cancel_event` the core records as an `interrupted`,
93
+ `--resume-run`-able run),
86
94
  `reserve_entity_ids`, `create_release`, `export`, `run_coverage`
87
95
  (a below-threshold coverage report returns `exit_code=1` in the result
88
96
  dict — a warning, not an exception), `preflight_backend`, `run_analysis`
@@ -110,12 +118,15 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
110
118
  the project-wide label summary behind the Files screen's `l` binding), which
111
119
  has no CLI reader at all. The
112
120
  Config screen (`operon/tui/screens/config.py`, nav key `6`) edits
113
- `config/profiles/*.yaml` (kind `qc` and kind `sequence_classification` —
114
- each kind has its own form, dispatched by the document's own kind, with the
115
- classification widgets in `operon/tui/screens/config_classification.py`
116
- and `actions.save_classification_profile` sharing `save_profile`'s version,
117
- snapshot and rollback machinery; a profile whose conditions nest deeper
118
- than one `any:`/`not:` level opens read-only) and single recipes inside
121
+ `config/profiles/*.yaml` (kinds `qc`, `sequence_classification` and
122
+ `taxonomy_coverage` — each kind has its own form, dispatched by the document's
123
+ own kind, with the classification widgets in `operon/tui/screens/config_classification.py`,
124
+ the coverage widgets in `operon/tui/screens/config_coverage.py`, and
125
+ `actions.save_classification_profile` / `actions.save_coverage_profile`
126
+ sharing `save_profile`'s version, snapshot and rollback machinery; a
127
+ classification profile whose conditions nest deeper than one `any:`/`not:`
128
+ level, or a coverage profile whose structure exceeds the flat grammar, opens
129
+ read-only) and single recipes inside
119
130
  `config/tools.yaml` through structured control-based forms (no free-text
120
131
  YAML): every save bumps the `version`, records the same content-addressed
121
132
  snapshot the CLI records (`qc_profiles` / `recipe_snapshots`), and
@@ -132,7 +143,9 @@ The project is licensed AGPL-3.0-or-later (`LICENSE` at the repo root).
132
143
  `config.py` (project configuration and directory layout), `files.py`
133
144
  (immutable manifest archival and verification), `profiles.py` +
134
145
  `rules.py` (versioned QC profiles and the decision engine),
135
- `workflow.py` (state machine and run logs), `tools.py` (external-tool
146
+ `workflow.py` (state machine and run logs), `pipeline.py` (the
147
+ four-stage ingest → standardize → QC → evaluate runner shared by
148
+ `run-pipeline`'s CLI and TUI), `tools.py` (external-tool
136
149
  recipes from `config/tools.yaml`), `taxonomy.py` + `coverage.py` (frozen
137
150
  NCBI Taxonomy snapshots and coverage denominators), `release.py` +
138
151
  `export.py` (immutable releases and selective exports), `lifecycle.py`
@@ -315,7 +328,7 @@ change:
315
328
  `docs/<language>/.readthedocs.yaml`, and the registry in
316
329
  `tests/unit/test_docs_projects.py`
317
330
 
318
- Version markers in docs (`operon` 0.8.4, database schema 2.11, metadata
331
+ Version markers in docs (`operon` 0.9.0, database schema 2.11, metadata
319
332
  schema 1.4) must match `pyproject.toml` and the code. Do not write the
320
333
  current values literally in Markdown sources: use the `myst_substitutions`
321
334
  references `{{ operon_version }}`, `{{ db_schema }}`, and
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2026 HYLi360.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification,
4
+ are permitted provided that the following conditions are met:
5
+
6
+ Redistributions of source code must retain the above copyright notice, this list
7
+ of conditions and the following disclaimer.
8
+
9
+ Redistributions in binary form must reproduce the above copyright notice, this
10
+ list of conditions and the following disclaimer in the documentation and/or
11
+ other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
17
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OperonDBS
3
- Version: 0.8.4
3
+ Version: 0.9.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
- License-Expression: AGPL-3.0-or-later
6
+ License-Expression: BSD-2-Clause
7
7
  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
@@ -61,6 +61,7 @@ docs/en/operations/ncbi-recovery-migration.md
61
61
  docs/en/operations/qc-performance.md
62
62
  docs/en/reference/behaviors-and-limitations.md
63
63
  docs/en/reference/cli-analysis.md
64
+ docs/en/reference/cli-config.md
64
65
  docs/en/reference/cli-decisions-reports.md
65
66
  docs/en/reference/cli-files-qc.md
66
67
  docs/en/reference/cli-project-metadata.md
@@ -119,6 +120,7 @@ docs/zh/operations/ncbi-recovery-migration.md
119
120
  docs/zh/operations/qc-performance.md
120
121
  docs/zh/reference/behaviors-and-limitations.md
121
122
  docs/zh/reference/cli-analysis.md
123
+ docs/zh/reference/cli-config.md
122
124
  docs/zh/reference/cli-decisions-reports.md
123
125
  docs/zh/reference/cli-files-qc.md
124
126
  docs/zh/reference/cli-project-metadata.md
@@ -154,12 +156,14 @@ operon/lifecycle.py
154
156
  operon/lineage.py
155
157
  operon/metadata_files.py
156
158
  operon/ncbi_reconcile.py
159
+ operon/pipeline.py
157
160
  operon/profiles.py
158
161
  operon/release.py
159
162
  operon/remotes.py
160
163
  operon/reports.py
161
164
  operon/rules.py
162
165
  operon/schema.py
166
+ operon/secrets.py
163
167
  operon/sequence_tools.py
164
168
  operon/shutdown.py
165
169
  operon/sql.py
@@ -176,6 +180,7 @@ operon/qc/__init__.py
176
180
  operon/qc/_alignment.pyx
177
181
  operon/qc/_parsers.pyx
178
182
  operon/qc/alignment.py
183
+ operon/qc/imports.py
179
184
  operon/qc/measure.py
180
185
  operon/qc/parsers.py
181
186
  operon/tui/__init__.py
@@ -194,6 +199,7 @@ operon/tui/screens/classify.py
194
199
  operon/tui/screens/common.py
195
200
  operon/tui/screens/config.py
196
201
  operon/tui/screens/config_classification.py
202
+ operon/tui/screens/config_coverage.py
197
203
  operon/tui/screens/coverage.py
198
204
  operon/tui/screens/decisions.py
199
205
  operon/tui/screens/derived_ops.py
@@ -205,9 +211,12 @@ operon/tui/screens/hits.py
205
211
  operon/tui/screens/home.py
206
212
  operon/tui/screens/import_wizard.py
207
213
  operon/tui/screens/labels.py
214
+ operon/tui/screens/ncbi_datasets.py
208
215
  operon/tui/screens/publish.py
209
216
  operon/tui/screens/run_external.py
210
217
  operon/tui/screens/runs.py
218
+ operon/tui/screens/table_import.py
219
+ operon/tui/screens/taxonomy.py
211
220
  tests/__init__.py
212
221
  tests/helpers.py
213
222
  tests/compatibility/__init__.py
@@ -236,6 +245,7 @@ tests/unit/__init__.py
236
245
  tests/unit/test_alignment_qc.py
237
246
  tests/unit/test_classify.py
238
247
  tests/unit/test_cli_edges.py
248
+ tests/unit/test_config_cli.py
239
249
  tests/unit/test_config_workflow_edges.py
240
250
  tests/unit/test_core_coverage_more.py
241
251
  tests/unit/test_coverage_edges.py
@@ -244,6 +254,7 @@ tests/unit/test_defect_registry.py
244
254
  tests/unit/test_defects_cli.py
245
255
  tests/unit/test_docs_projects.py
246
256
  tests/unit/test_docs_versions.py
257
+ tests/unit/test_env_audit.py
247
258
  tests/unit/test_environment.py
248
259
  tests/unit/test_environment_capture.py
249
260
  tests/unit/test_execution.py
@@ -261,6 +272,7 @@ tests/unit/test_ncbi_coverage_gaps.py
261
272
  tests/unit/test_ncbi_edge_cases.py
262
273
  tests/unit/test_ncbi_reconcile_edges.py
263
274
  tests/unit/test_parser_edge_paths.py
275
+ tests/unit/test_pipeline.py
264
276
  tests/unit/test_qc_and_rules.py
265
277
  tests/unit/test_qc_edges_more.py
266
278
  tests/unit/test_qc_measure.py
@@ -275,6 +287,7 @@ tests/unit/test_schema_2_10.py
275
287
  tests/unit/test_schema_2_11.py
276
288
  tests/unit/test_schema_2_9.py
277
289
  tests/unit/test_schema_and_metadata.py
290
+ tests/unit/test_secrets.py
278
291
  tests/unit/test_sequence_tools.py
279
292
  tests/unit/test_sequences.py
280
293
  tests/unit/test_shutdown.py
@@ -287,14 +300,22 @@ tests/unit/test_tools_edges.py
287
300
  tests/unit/test_tools_execution_more.py
288
301
  tests/unit/test_tui.py
289
302
  tests/unit/test_tui_analyze.py
303
+ tests/unit/test_tui_button_dispatch.py
290
304
  tests/unit/test_tui_cli_parity.py
291
305
  tests/unit/test_tui_config.py
292
306
  tests/unit/test_tui_derived_ops.py
293
307
  tests/unit/test_tui_flows_more.py
308
+ tests/unit/test_tui_import_qc.py
294
309
  tests/unit/test_tui_m3_loop.py
310
+ tests/unit/test_tui_ncbi_datasets.py
311
+ tests/unit/test_tui_pipeline.py
295
312
  tests/unit/test_tui_publish.py
296
313
  tests/unit/test_tui_screens_more.py
297
314
  tests/unit/test_tui_splash_terminal.py
315
+ tests/unit/test_tui_standardize.py
316
+ tests/unit/test_tui_table_import.py
317
+ tests/unit/test_tui_taxonomy.py
298
318
  tests/unit/test_tui_writes.py
319
+ tests/unit/test_user_config.py
299
320
  tests/unit/test_views_release_reports_edges.py
300
321
  tests/unit/test_workflow_cli.py
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: OperonDBS
3
- Version: 0.8.4
3
+ Version: 0.9.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
- License-Expression: AGPL-3.0-or-later
6
+ License-Expression: BSD-2-Clause
7
7
  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
@@ -95,7 +95,7 @@ def source_constant(module: str, name: str) -> str:
95
95
  try:
96
96
  imported = __import__(f"operon.{module}", fromlist=[name])
97
97
  return str(getattr(imported, name))
98
- except Exception:
98
+ except (ImportError, AttributeError):
99
99
  source = (REPO_ROOT / "operon" / f"{module}.py").read_text(encoding="utf-8")
100
100
  match = re.search(rf'^{name} = "([^"]+)"', source, re.MULTILINE)
101
101
  if match is None:
@@ -120,6 +120,8 @@ How the principles map to implementations:
120
120
  | `operon/profiles.py` | Loads and validates the versioned YAML profiles shared by the rule engine and taxonomy coverage |
121
121
  | `operon/metadata_files.py` | Compatibility notice for the retired live metadata TSV directory; SQLite is the only writable metadata source |
122
122
  | `operon/qc/measure.py` | Project-independent `operon qc-measure` measurement path whose JSON payload is imported back through `operon import-qc` |
123
+ | `operon/qc/imports.py` | The shared `operon import-qc` core: JSON-payload/TSV detection, validation against the manifest, metric insertion, QC-state recomputation and the run record; `plan_qc_import` is the write-free preview the TUI shows |
124
+ | `operon/pipeline.py` | The shared `operon run-pipeline` core: the ingest → standardize → QC → evaluate stages, `plan_pipeline`'s write-free preflight (resolved profile, entity checks, the curated-decision gate) and a `progress` callback the CLI prints |
123
125
  | `operon/classify.py` | `sequence_classification` profiles: labels per sequence from stored alignment hits into `sequence_labels` |
124
126
  | `operon/sequence_tools.py` | `extract-domains` / `select-sequences`: materializes FASTA subsets from stored alignment intervals |
125
127
  | `operon/fanout.py` | Data-derived fan-out of registered sequence files into per-unit FASTAs under `analysis/derived/` with `file_lineage` edges |
@@ -19,6 +19,10 @@ operon import table --table organisms --file organisms.csv \
19
19
  --on-conflict update --yes
20
20
  ```
21
21
 
22
+ The same template/preview flow is available in the TUI: the Home screen's
23
+ *Import table* dialog generates templates and runs the mandatory preview
24
+ before Confirm (see the reference's Import table section).
25
+
22
26
  Import behavior:
23
27
 
24
28
  - Only manually managed entity/accession tables are accepted. System-managed tables such as `files`, QC results, and decisions cannot be overwritten by table import.
@@ -94,4 +94,56 @@ recipes:
94
94
 
95
95
  See [Recipe Field Reference](../reference/recipe-fields.md#slurm-resource-overrides) for the complete field list, including the `array` / `array_concurrency` job-array keys, which apply equally to local Slurm and to remote Slurm over SSH.
96
96
 
97
- > The automated tests for Slurm and SSH use simulated sbatch/squeue and in-memory SSH/SFTP implementations. The SSH/SFTP, remote-only analysis, and remote Slurm paths were also smoke-tested on 2026-09-04 against a Linux OpenSSH login node, a shared GPFS filesystem, and a Slurm compute node. Each deployment should still run a short local smoke task to validate its host keys, filesystem visibility, partitions, submission, cancellation, polling, and output retrieval.
97
+ ## Smoke-testing a deployment
98
+
99
+ The automated tests for Slurm and SSH use simulated sbatch/squeue and in-memory SSH/SFTP implementations. The SSH/SFTP, remote-only analysis, and remote Slurm paths have also been smoke-tested against real clusters:
100
+
101
+ - 2026-09-04 — Linux OpenSSH login node, shared GPFS filesystem, Slurm compute node;
102
+ - 2026-09-20 — OpenSSH login node with `sbatch`/`squeue`/`sacct` and a `cu` partition, mirror and compute root on one shared filesystem: `evict` → remote-only `analyze` of an HMMER recipe → `pull`, over 13 MB of input.
103
+
104
+ Each deployment should still run a short task of its own; the checklist, reference run and pitfalls below are what validated the 2026-09-20 deployment.
105
+
106
+ ### Checklist
107
+
108
+ 1. **Mirror reachability and host key.** `operon remotes` must list the mirror as `ok`; an unknown host is rejected until its key is in `known_hosts` or `host_key_sha256` is pinned.
109
+ 2. **One filesystem, two node classes.** `remote_root` (inherited from `storage_remote` by default) must be visible from the login node that runs `sbatch` and from the compute nodes.
110
+ 3. **Scheduler.** `sbatch`, `squeue` and `sacct` must be in the login node's non-interactive `PATH`, and the configured partition must exist.
111
+ 4. **Tools on the compute side.** Every recipe `executable` must be on the `PATH` the submitted job inherits. `operon tools-check` probes the local machine only, so it cannot validate the remote side; a binary reachable only from an interactive shell, or only from a conda environment the non-interactive shell does not activate, fails at probe or submit time.
112
+ 5. **Reference database.** Deploy it in advance at the remote target path and declare `database_checksum`; prefer a project-relative `database` so the path is mapped into the remote root (see [Databases on SSH remotes](../reference/recipe-fields.md#databases-on-ssh-remotes)).
113
+ 6. **A short recipe over one small file**, so the smoke run takes minutes rather than hours.
114
+
115
+ To also exercise remote-only input consumption, configure `execution.ssh.storage_remote` as in [Remote-First Operation](remote-first.md) and evict the file before the run.
116
+
117
+ ### Reference smoke run
118
+
119
+ ```bash
120
+ operon evict --remote mycluster --file-id FIL_000001 # input becomes REMOTE_ONLY
121
+ operon analyze --analysis <recipe> --entity-id <entity> --threads 24 --backend ssh
122
+ operon analyze --analysis <recipe> --entity-id <entity> --threads 24 --backend ssh # must report cached
123
+ operon pull --remote mycluster --file-id FIL_000001 # restore the local bytes
124
+ ```
125
+
126
+ Then check both planes:
127
+
128
+ | Where | What to verify |
129
+ |---|---|
130
+ | `analysis_jobs` | `launcher = [ssh:user@host]`, `status = completed`, `output_sha256`, `environment_id`, `recipe_snapshot_id`, and a `command` naming the resolved database |
131
+ | `execution_details` | `scheduler_job_id`, `slurm_elapsed_seconds`, `host`, and the remote `script` path under `remote_root/logs/` |
132
+ | `execution_environments` | the probe describes the **compute node** (CPU model, cpuset), not the login node |
133
+ | remote host | `logs/<run_id>.sbatch` contains rewritten paths only — the `cd` and every project path inside `remote_root`; `<run_id>.exitcode` holds `0` |
134
+ | both sides | the retrieved output's SHA-256 equals `sha256sum` of the remote file |
135
+ | local `logs/` | `<run_id>.stdout.log` / `.stderr.log` are copies retrieved from the remote job |
136
+ | `changes` / `locations` | the eviction to `REMOTE_ONLY` and the restore to `CHECKSUM_VERIFIED` are audited, and the placeholder under `.operon/placeholders/` is gone after `pull` |
137
+
138
+ A second identical run must report `cached` for the file instead of submitting another job; if it resubmits, the cache identity changed (a different backend or host/root, an edited recipe, or a different resolved `database`).
139
+
140
+ ### Pitfalls
141
+
142
+ - **A `database` outside the project root is never mapped.** Absolute values — including `~/...`, which is expanded before mapping — are passed through verbatim, so the compute side must have that exact path. A recipe that works locally with `/data/db/Pfam-A.hmm` or `~/resources/hmm/PF00010.hmm` fails remotely. A project-relative value is mapped into `remote_root` instead, which is what most recipes want.
143
+ - **`--dry-run` validates what needs no command execution.** The local database existence check runs, and for a remote `reference` database so does the provisioning stat, so a database that exists nowhere fails the preview with the message a real run reports — `reference database not found: <path>; edit config/tools.yaml`, or `remote reference database is not provisioned at <path>` — and the command exits non-zero. A remote `mutable_cache` has nothing to verify yet, and the tool version is still reported as `not probed (backend=…)` because probing it would execute a command. Validate a deployment with one real file, not with `--dry-run`.
144
+ - **A remote `reference` database must declare `database_checksum`.** Without it the run is rejected before anything is submitted: `remote reference databases require database_checksum so cache identity does not depend on a missing local path`.
145
+ - **The reference database is not a manifest file.** `push`, `evict` and `pull` move ingested files only, so `operon remotes` does not count the database and a new mirror root needs it redeployed by hand.
146
+ - **Remote-only semantics come from the backend, not from the file's status.** Any `ssh` run with a non-empty `remote_root` skips the local database existence check, requires `database_checksum`, and mixes the SSH location into the database cache identity. Results are therefore not shared between the `local` and `ssh` backends, or between two hosts/roots — a backend switch recomputes by design.
147
+ - **The input never travels back on its own.** A locally present input is uploaded over SFTP; a `REMOTE_ONLY` input is checked against the local manifest, the remote manifest and the live remote bytes, then consumed in place. Either way the bytes stay where they are, and `pull` remains an explicit step.
148
+ - **Tool outputs may embed the command that produced them.** HMMER's `--tblout`/`--domtblout` banner records the query and target paths, the full argv, the working directory and the date, so the same input analyzed locally and remotely yields different bytes even when the hit table is identical (observed on one file: 135 identical data lines, 5 differing banner lines, `output_sha256` `a64cc0c5…` → `1a0fc453…`). Treat `output_sha256` as host- and path-specific: a re-run on another backend overwrites the recorded output, and the older job row's hash then no longer matches the file on disk — such a row is marked `superseded` the next time it is hit. This is another reason the cache identity includes the SSH location.
149
+ - **Tool versions are probed through the backend, so keep the two installations compatible.** `qc-measure` payloads are the one place where a version mismatch is only a warning; for `analyze` results, a remote tool of a different version changes the recorded `tool_version` and therefore the cache identity.
@@ -124,6 +124,15 @@ operon pull --remote mycluster --file-id FIL_000001
124
124
 
125
125
  `REMOTE_ONLY` files make the local database and the remote mirror root two separate backup targets; see [Backup, Migration, and Resumption](backup-migration.md).
126
126
 
127
+ ## Common pitfalls
128
+
129
+ - **The reference database is not mirrored.** `push`, `evict` and `pull` move manifest files only, so a recipe `database` must be deployed at the remote target path by hand — with a `database_checksum` for a `reference` database — and redeployed whenever the mirror moves to another host or root.
130
+ - **Keep `database` project-relative.** An absolute or `~/...` value reaches the remote command verbatim and must exist at that exact path there; a project-relative value is mapped into the remote root instead.
131
+ - **`--dry-run` only previews.** It skips the database checks and does not probe the tool through the backend, so a deployment is validated by the first real run, not by a dry run.
132
+ - **`analyze` hydrates nothing.** A `REMOTE_ONLY` input is consumed in place and stays `REMOTE_ONLY`; `standardize`, `release` and `export` still need local bytes, so `pull` first.
133
+ - **Results are location-scoped.** The database identity includes the SSH host and root, so the same recipe does not share cached results between the `local` and `ssh` backends, or between two hosts.
134
+ - **A backend switch is not just a speed setting.** Any `ssh` run with a non-empty `remote_root` requires `database_checksum` and skips the local database existence check. See [Remote Execution with Slurm and SSH](remote-execution.md#pitfalls) for the field-level rules and the rest of the list.
135
+
127
136
  ## Limitations
128
137
 
129
138
  - `run-external` and `analyze` are synchronous and blocking: the local CLI process holds the remote job for its whole lifetime. Interrupting the process or hitting `--timeout` cancels the remote job (`scancel` for remote Slurm, TERM/KILL to the process group for direct SSH). There is no submit-and-disconnect mode that recovers a job later.
@@ -28,7 +28,7 @@ Import, compilation, and reporting are explicit commands. Imported source files
28
28
 
29
29
  ## Prepare a coverage profile
30
30
 
31
- `operon init` creates `config/profiles/coverage_viridiplantae_v1.yaml` as an example. It is a template, not a universal standard. Before production use, review the clade, exclusion rules, and thresholds, and save the result under a filename that reflects the study scope. QC and coverage profiles share the directory but are distinguished by the required `kind` field.
31
+ `operon init` creates `config/profiles/coverage_viridiplantae_v1.yaml` as an example. It is a template, not a universal standard. Before production use, review the clade, exclusion rules, and thresholds, and save the result under a filename that reflects the study scope. QC and coverage profiles share the directory but are distinguished by the required `kind` field. The TUI's Config screen (`operon tui`, key `6`, QC Profiles tab) edits coverage profiles with the same version bump and content-addressed snapshot recording as the CLI; a profile whose structure exceeds the form opens read-only there. Hand-editing the YAML file remains fully supported.
32
32
 
33
33
  ```yaml
34
34
  kind: taxonomy_coverage
@@ -94,6 +94,10 @@ After import:
94
94
 
95
95
  Repeating an import with the same version and bytes reuses the snapshot. The same version label with different bytes is a conflict and is rejected.
96
96
 
97
+ The TUI offers the same import on the Coverage screen (*Import taxonomy…*
98
+ button) with the equivalent command preview; a running import cannot be
99
+ interrupted from the TUI.
100
+
97
101
  ## Compile the immutable denominator
98
102
 
99
103
  ```bash
@@ -112,6 +116,11 @@ taxonomy/reference_sets/
112
116
  └── coverage_viridiplantae_v1@2026-08-01.provenance.json
113
117
  ```
114
118
 
119
+ The TUI offers the same compilation on the Coverage screen (*Compile
120
+ reference set…* button): the profile and the taxonomy version are picked
121
+ from dropdowns that list only READY snapshots; a running compile cannot be
122
+ interrupted from the TUI.
123
+
115
124
  Each TSV row is a target taxon with fixed columns:
116
125
 
117
126
  ```text
@@ -131,7 +131,7 @@ remaining bullets are intended-but-implicit semantics or accepted limitations.
131
131
  - **A control ignores a click while its press animation runs.** Textual's `Button` swallows a click while the button still carries its `-active` press effect (roughly 0.2 s), so a rapid double-click on the same button is lost; the TUI's dialogs are built around one Confirm per action, and tests wait for the effect to clear before clicking again.
132
132
  - **The Files screen's derived-artifact dialogs leave registration to adopt.** *Extract domains* and *Select sequences* write their FASTA through the core and then hand the path to the adopt dialog (`derived_from` = the source file), because neither command registers its output — the same two-step flow as the CLI. The adopt dialog's manifest mode requires a *Preview* before Confirm (the parsed item count is shown), and `fanout`'s dry run is a mandatory first step that performs the real preflight and writes nothing at all (no run row either); changing an input invalidates the preview and disables Confirm again.
133
133
  - **The classification-profile form stops at one level of `any:`/`not:`.** The Config screen's `sequence_classification` editor models flat conditions, one `any:` group and one `not:` negation — the same forms the qc editor offers for its rules. A profile that nests deeper (or whose `sources`/`rules` are not mappings) opens read-only: the reason is printed, *Save profile* is disabled, and the file is never rewritten, so hand-written structure survives every visit to the editor. Unknown keys are preserved verbatim at every level, and the composed document keeps the original key order.
134
- - **A profile's kind belongs to the file, not the editor.** The TUI routes a document to the qc or the classification form by the `kind` it finds on disk, and a save refuses to cross kinds (editing a `kind: qc` file with a classification document, or vice versa, raises instead of writing). `taxonomy_coverage` profiles stay hand-edited.
134
+ - **A profile's kind belongs to the file, not the editor.** The TUI routes a document to the qc, classification or coverage form by the `kind` it finds on disk, and a save refuses to cross kinds (editing a `kind: qc` file with a classification document, or vice versa, raises instead of writing). The `taxonomy_coverage` editor models the flat coverage grammar (NCBI source, root TaxIDs, family/genus ranks, exclusion filters, per-rank thresholds); a profile whose structure exceeds the form opens read-only, and unknown keys are preserved verbatim at the document and section level.
135
135
 
136
136
  ### TimeTree
137
137
 
@@ -250,7 +250,7 @@ remaining bullets are intended-but-implicit semantics or accepted limitations.
250
250
  - **Only some symlinks are rebased.** Absolute symlinks under `standardized/` that point inside the project are rewritten to relative targets when the backup is created; relative links, links elsewhere in the tree and absolute targets outside the project keep their exact text, and there is no restore command (`backup.py`).
251
251
  - **`backup verify` rejects unexpected extra files**, not only missing or changed ones (`backup.py`).
252
252
  - **The wizard holds the write lock while hashing and copying.** Ingest happens inside one large transaction; on failure the database rolls back, buffered JSONL records are discarded, and only newly created `raw/` targets are deleted — pre-existing targets stay (`import_wizard.py`).
253
- - **The wizard re-hashes inputs, burns IDs and needs a user name.** Each input is hashed two to three times and the first pass runs outside the write lock; `db.next_id()` reserves and commits IDs while prompts are still open, so a cancelled wizard permanently burns IDs; the actor is `os.environ.get("USER")`, so container or cron runs record a NULL actor; and new entities are forced to `METADATA_VALIDATED` (`import_wizard.py`, `database.py`).
253
+ - **The wizard re-hashes inputs, burns IDs and needs a user name.** Each input is hashed two to three times and the first pass runs outside the write lock; `db.next_id()` reserves and commits IDs while prompts are still open, so a cancelled wizard permanently burns IDs; the actor is the resolved audit actor (see [user configuration](cli-config.md)), so container or cron runs without `USER`, a local account entry, or `identity.actor` record a NULL actor; and new entities are forced to `METADATA_VALIDATED` (`import_wizard.py`, `database.py`).
254
254
  - **The wizard needs a TTY and accepts only regular files** (no directory artifacts). If another process consumes a shown ID in the meantime, preflight raises and the wizard must be restarted (`import_wizard.py`).
255
255
  - **Known issue: the wizard breaks when its draft outlives the database state.** Repopulating a page assigns the draft's remembered IDs to the pickers; if an entity was retired or removed while the wizard was open, the `Select` value is no longer among the freshly loaded options and `InvalidSelectValueError` is raised on the UI thread inside the worker callback, so the page never renders and no inline error appears. For the same reason a failed startup leaves navigation enabled with an empty reserved-ID map, and pressing Next with a typed organism name then raises an uncaught `KeyError`. Leaving the sequencing/annotation page clears only the enable checkbox, so stale widget text can reappear when the section is re-enabled (`import_wizard.py`, TUI).
256
256
  - **The demo project is byte-deterministic and deliberately broken.** The synthetic project uses a fixed seed (20260816), fixed contig lengths and 400×100 bp reads with hard-coded IDs (with a gap at `ANN_000002`), release `2026.08.demo` and project `PRJ_DEMO_001`; it ships a broken annotation (601-nt CDS, dangling mRNA parent), constant `'I'` read quality and an R2 that is unrelated random DNA rather than the reverse complement; its sources live in `examples/synthetic_source`, which `full` backups copy (`demo.py`, `backup.py`).
@@ -57,7 +57,7 @@ Outside `--dry-run`, `analyze` prints one progress line per file as processing a
57
57
 
58
58
  Slurm array submission: when the recipe's `slurm:` block sets `array: true`, the selected executor supports job arrays (the local Slurm backend, or the SSH backend with `scheduler: slurm`), and at least two files in the batch miss the cache, those files are submitted as a single Slurm job array instead of one job per file; `array_concurrency: N` throttles how many array tasks run concurrently (Slurm `%N`). In every other case the per-file submission path is used unchanged. Each file still gets its own `workflow_runs` row, with the per-task scheduler job ID recorded as `<array_id>_<task_index>`. Cache behavior is unchanged: array participation never enters the parameter fingerprint or cache identity, so cache hits and output adoption work identically with array submission on or off. See "Slurm array submission" in the [Recipe field reference](recipe-fields.md).
59
59
 
60
- `--dry-run` lists the plan without execution. Status values are `cached` (completed cache hit), `adoptable` (verified old output will be adopted), or `planned` (execution will run). The output column contains planned output paths, and `tool_version` contains the detected version.
60
+ `--dry-run` lists the plan without execution. Status values are `cached` (completed cache hit), `adoptable` (verified old output will be adopted), or `planned` (execution will run). The output column contains planned output paths, and `tool_version` contains the detected version on the local backend; a non-local backend reports `not probed (backend=…)` because the probe itself would open a connection. A dry run does perform the validations that need no command execution — the recipe database's local existence, a remote reference database's provisioning (a stat over the backend), and the `database_checksum` rule — so a plan that cannot execute reports the same error a real run would, and the command exits non-zero.
61
61
 
62
62
  `--param` can set only parameters declared by the recipe. Missing required parameters, unknown parameters, repeated values, or values failing `pattern`/`choices` are configuration errors. Default `busco_lineage` usage:
63
63
 
@@ -0,0 +1,152 @@
1
+ # User configuration
2
+
3
+ `operon` keeps user-level settings in one optional YAML file outside the
4
+ project. It never replaces project configuration: `project.yaml` still owns the
5
+ storage layout, database path, resources, and execution backends, while the user
6
+ file owns the handful of settings that belong to *you* rather than to a project.
7
+
8
+ ```{note}
9
+ The file is optional. Without it, built-in defaults apply and the per-user
10
+ identity falls back to the environment, exactly as before.
11
+ ```
12
+
13
+ ## Location
14
+
15
+ The user configuration follows the XDG Base Directory specification:
16
+
17
+ ```text
18
+ $XDG_CONFIG_HOME/operon/config.yml # typically ~/.config/operon/config.yml
19
+ ```
20
+
21
+ - `XDG_CONFIG_HOME` unset or empty → `~/.config/operon/config.yml`.
22
+ - There is no `OPERON_CONFIG_HOME`: the path is fully determined by XDG.
23
+ - `operon` never creates `~/.operon`, and there is no per-project user file.
24
+ - The file is written with mode `0600`, its directory with `0700`.
25
+ - `operon config` is project independent: it never opens `operon.sqlite` and
26
+ works before `operon init` and outside any project directory.
27
+
28
+ ## Precedence
29
+
30
+ Command-line flags win over environment variables, environment variables win
31
+ over the file, and the file wins over built-in defaults.
32
+
33
+ | Setting | Flag | Environment | User file | Fallback |
34
+ | --- | --- | --- | --- | --- |
35
+ | Audit actor | `--actor` | `OPERON_ACTOR`, `USER`, `LOGNAME`, `USERNAME` | `identity.actor` | the local account (`getpass`), then no actor |
36
+ | NCBI contact | `--email` | `NCBI_EMAIL` | `ncbi.email` | unset |
37
+ | NCBI API key | `--api-key` | `NCBI_API_KEY` | never stored in the file | a stored secret (see below) |
38
+ | Terminal graphics | — | `OPERON_SPLASH` | `ui.splash` | `auto` (detection) |
39
+
40
+ Two details are worth stating explicitly:
41
+
42
+ - The local account sits between the environment and the file: on a normal
43
+ login the actor is still `$USER`, and `identity.actor` only fills the gap in
44
+ containers and cron jobs where no login variable and no password entry exist.
45
+ - Operations that require an actor (`retire --apply`, `restore --apply`, the
46
+ TUI lifecycle and curate dialogs) fail with a clear error instead of writing
47
+ a nameless audit row.
48
+
49
+ ## Keys
50
+
51
+ | Key | Type | Default | Meaning |
52
+ | --- | --- | --- | --- |
53
+ | `schema_version` | integer | `1` | Document version; written by `operon config init` |
54
+ | `identity.actor` | string | `""` | Fallback audit actor |
55
+ | `ncbi.email` | string | `""` | NCBI contact address for API requests |
56
+ | `ui.splash` | choice | `auto` | `auto`, `text`, `blocks`, or `kitty` |
57
+
58
+ Unknown keys are preserved in the file but ignored by this version, and
59
+ `operon config check` reports them. Keys that look like secret material
60
+ (`…api_key`, `token`, `secret`, `password`, `credential`) are refused by
61
+ `operon config set`: credentials belong in a secret backend.
62
+
63
+ ## Commands
64
+
65
+ ```bash
66
+ operon config path
67
+ operon config show [--effective] [--json]
68
+ operon config get KEY
69
+ operon config set KEY VALUE
70
+ operon config unset KEY
71
+ operon config check
72
+ operon config init [--force]
73
+ operon config secret list
74
+ operon config secret set NAME
75
+ operon config secret get NAME
76
+ operon config secret clear NAME
77
+ ```
78
+
79
+ - `path`: prints the resolved file path.
80
+ - `show`: prints the stored document (defaults when the file does not exist).
81
+ It never creates the file. With `--effective` it prints the value that wins
82
+ for every setting, plus where that value comes from; `--json` switches the
83
+ output format.
84
+ - `get`: prints one value and exits `0`; a key without a value prints nothing
85
+ and exits `1`, like `git config`.
86
+ - `set` / `unset`: validate the key and the value before writing, then write
87
+ atomically with user-only permissions. `unset` restores the built-in default.
88
+ - `check`: validates the YAML, warns about a group/world-readable file and
89
+ about unknown keys, fails on secret material, and always prints the effective
90
+ settings — the quickest way to see which layer wins.
91
+ - `init`: writes the default document; refuses to overwrite without `--force`.
92
+
93
+ ## Secrets
94
+
95
+ Credentials never live in the user file and never have to live in the
96
+ environment. `operon` stores them through the first available system backend:
97
+
98
+ 1. `secret-tool` — the Secret Service (GNOME Keyring, KWallet; `libsecret-tools`).
99
+ 2. `systemd-creds --user` — encrypted credentials under
100
+ `$XDG_CONFIG_HOME/operon/secrets/`.
101
+ 3. `/usr/bin/security` — the macOS Keychain.
102
+
103
+ All three are used through `subprocess`; no new runtime dependency is required.
104
+ With none of them available, `operon` stops with a clear error and tells you to
105
+ pass `--api-key` or set `NCBI_API_KEY`. The key is resolved as
106
+ `--api-key` > `NCBI_API_KEY` > stored secret.
107
+
108
+ ```bash
109
+ # Store without exposing the value in argv or shell history
110
+ printf '%s' "$NCBI_TOKEN" | operon config secret set ncbi.api_key
111
+ operon config secret list
112
+ operon config secret clear ncbi.api_key
113
+ ```
114
+
115
+ `secret set` reads the value from stdin, or prompts with hidden input when a
116
+ terminal is attached; the value is never accepted as a command-line argument.
117
+ `secret list` shows backend availability and whether each secret is set — never
118
+ a value. `secret get` prints the value for scripts, with a warning when stdout
119
+ is a terminal.
120
+
121
+ ```{note}
122
+ `systemd-creds --user` binds a credential to the user, the machine ID and the
123
+ kernel's boot ID. On a shared HPC home it may therefore not decrypt on another
124
+ node: for remote execution prefer `NCBI_API_KEY` in the environment or
125
+ `--api-key` on the command line.
126
+ ```
127
+
128
+ ## Examples
129
+
130
+ ```bash
131
+ # Fallback identity for containers and cron jobs
132
+ operon config set identity.actor alice
133
+
134
+ # NCBI contact address, then the API key in the secret backend
135
+ operon config set ncbi.email you@example.org
136
+ printf '%s' "$NCBI_TOKEN" | operon config secret set ncbi.api_key
137
+
138
+ # Skip graphics detection for this user without an environment variable
139
+ operon config set ui.splash text
140
+
141
+ # What wins right now, and why
142
+ operon config show --effective
143
+ ```
144
+
145
+ ## Environment variables are not deprecated
146
+
147
+ Every variable keeps working and keeps winning over the file. The variables
148
+ `operon` reads directly are audited and documented in
149
+ `tests/unit/test_env_audit.py`: an unannotated `os.environ` read fails that
150
+ test, so any new variable has to be reviewed and documented before it ships.
151
+ Environment capture for `environment_id` (`PROBE_ENV_VARS`) and the HTTP/SSH
152
+ proxy variables read by `requests`/`paramiko` are unaffected.