pen-stack 5.1.0__tar.gz → 5.2.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 (310) hide show
  1. {pen_stack-5.1.0 → pen_stack-5.2.0}/CHANGELOG.md +33 -0
  2. {pen_stack-5.1.0 → pen_stack-5.2.0}/CITATION.cff +1 -1
  3. {pen_stack-5.1.0 → pen_stack-5.2.0}/PKG-INFO +18 -2
  4. {pen_stack-5.1.0 → pen_stack-5.2.0}/README.md +17 -1
  5. pen_stack-5.2.0/configs/genotoxicity_oracle.yaml +48 -0
  6. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/oracles/scope_cards.yaml +13 -0
  7. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/__init__.py +1 -1
  8. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/cite.py +6 -0
  9. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/delivery_immunology.py +12 -2
  10. pen_stack-5.2.0/pen_stack/planner/genotoxicity_oracle.py +112 -0
  11. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack.egg-info/PKG-INFO +18 -2
  12. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack.egg-info/SOURCES.txt +5 -0
  13. pen_stack-5.2.0/prereg/SHA256_LOCK_ws_genotox.json +8 -0
  14. pen_stack-5.2.0/prereg/ws_genotox.yaml +41 -0
  15. {pen_stack-5.1.0 → pen_stack-5.2.0}/pyproject.toml +1 -1
  16. pen_stack-5.2.0/scripts/p52_build_genotox_oracle.py +112 -0
  17. {pen_stack-5.1.0 → pen_stack-5.2.0}/LICENSE +0 -0
  18. {pen_stack-5.1.0 → pen_stack-5.2.0}/MANIFEST.in +0 -0
  19. {pen_stack-5.1.0 → pen_stack-5.2.0}/bench/run.py +0 -0
  20. {pen_stack-5.1.0 → pen_stack-5.2.0}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
  21. {pen_stack-5.1.0 → pen_stack-5.2.0}/benchmarks/genome_writing_bench/README.md +0 -0
  22. {pen_stack-5.1.0 → pen_stack-5.2.0}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
  23. {pen_stack-5.1.0 → pen_stack-5.2.0}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  24. {pen_stack-5.1.0 → pen_stack-5.2.0}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
  25. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/atlas_families.yaml +0 -0
  26. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/bridge_offtarget_profile.yaml +0 -0
  27. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/cargo_polish.yaml +0 -0
  28. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/cell_types.yaml +0 -0
  29. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/datasets.yaml +0 -0
  30. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/delivery_constraints.yaml +0 -0
  31. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/delivery_rules.yaml +0 -0
  32. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/delivery_vehicles.yaml +0 -0
  33. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/gates_v3.yaml +0 -0
  34. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/gsh_validated_heldout.yaml +0 -0
  35. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/intent_weights.yaml +0 -0
  36. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/known_unknowns.yaml +0 -0
  37. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/llm.yaml +0 -0
  38. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/monitor_queries.yaml +0 -0
  39. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/rules/delivery.yaml +0 -0
  40. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/rules/fold.yaml +0 -0
  41. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/rules/multiplex.yaml +0 -0
  42. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/rules/payload.yaml +0 -0
  43. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/rules/reachability.yaml +0 -0
  44. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/score_axes.yaml +0 -0
  45. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/target_sites.yaml +0 -0
  46. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/universe_crosswalk.yaml +0 -0
  47. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/write_types.yaml +0 -0
  48. {pen_stack-5.1.0 → pen_stack-5.2.0}/configs/wtkb_curated.yaml +0 -0
  49. {pen_stack-5.1.0 → pen_stack-5.2.0}/data/curated/bridge_offtarget_energetics.json +0 -0
  50. {pen_stack-5.1.0 → pen_stack-5.2.0}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  51. {pen_stack-5.1.0 → pen_stack-5.2.0}/data/curated/gene_coords.parquet +0 -0
  52. {pen_stack-5.1.0 → pen_stack-5.2.0}/data/curated/unified_editor_universe.parquet +0 -0
  53. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/BACKLOG.md +0 -0
  54. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/DEPLOY.md +0 -0
  55. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/INFRA.md +0 -0
  56. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/MCP.md +0 -0
  57. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/RELEASING.md +0 -0
  58. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/REPRO.md +0 -0
  59. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/agent.md +0 -0
  60. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/alphagenome_feasibility.md +0 -0
  61. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/benchmark_circularity.md +0 -0
  62. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/cards/atlas.md +0 -0
  63. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/cards/durability.md +0 -0
  64. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/cards/safety.md +0 -0
  65. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/co_scientist.md +0 -0
  66. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/delivery.md +0 -0
  67. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/dissemination.md +0 -0
  68. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/environment.md +0 -0
  69. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/index.md +0 -0
  70. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/mechanistic_constraints.md +0 -0
  71. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/oracles.md +0 -0
  72. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/positioning.md +0 -0
  73. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/private_data_formats.md +0 -0
  74. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/quickstart.md +0 -0
  75. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/rules.md +0 -0
  76. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/scope.md +0 -0
  77. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/scorecard.md +0 -0
  78. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/tutorials/compare-families.md +0 -0
  79. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/tutorials/score-deliverability.md +0 -0
  80. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/tutorials/where-can-i-write.md +0 -0
  81. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  82. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/uncertainty.md +0 -0
  83. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/verify.md +0 -0
  84. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/world_model.md +0 -0
  85. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/writer_verification.md +0 -0
  86. {pen_stack-5.1.0 → pen_stack-5.2.0}/docs/wtkb.md +0 -0
  87. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/_resources.py +0 -0
  88. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/adapt/__init__.py +0 -0
  89. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/adapt/finetune.py +0 -0
  90. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/adapt/ingest.py +0 -0
  91. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/adapt/pipeline.py +0 -0
  92. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/adapt/recalibrate.py +0 -0
  93. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/adapt/report.py +0 -0
  94. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/__init__.py +0 -0
  95. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/co_scientist.py +0 -0
  96. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/epistemic.py +0 -0
  97. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/guardrails.py +0 -0
  98. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/mcp_server.py +0 -0
  99. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/orchestrator.py +0 -0
  100. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/pen_agent.py +0 -0
  101. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/scope.py +0 -0
  102. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/agent/tools.py +0 -0
  103. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/__init__.py +0 -0
  104. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/build_wtkb.py +0 -0
  105. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/crosslink.py +0 -0
  106. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/expand.py +0 -0
  107. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/schema.py +0 -0
  108. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/scorecard.py +0 -0
  109. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/universe.py +0 -0
  110. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/variant_propose.py +0 -0
  111. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/atlas/writer_verify.py +0 -0
  112. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/__init__.py +0 -0
  113. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/activity.py +0 -0
  114. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/cli.py +0 -0
  115. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/fold_qc.py +0 -0
  116. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/guide_qc.py +0 -0
  117. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/ingest.py +0 -0
  118. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/offtarget.py +0 -0
  119. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/offtarget_energetics.py +0 -0
  120. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/ortholog_screen.py +0 -0
  121. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/bridge/pipeline.py +0 -0
  122. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/cli.py +0 -0
  123. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/data/__init__.py +0 -0
  124. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/data/encode.py +0 -0
  125. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/data/genome.py +0 -0
  126. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/data/ingest_chromatin.py +0 -0
  127. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/data/ingest_integration.py +0 -0
  128. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/data/ingest_safety_annot.py +0 -0
  129. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/data/ingest_trip.py +0 -0
  130. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/env/__init__.py +0 -0
  131. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/env/genome_writing_env.py +0 -0
  132. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/env/policies.py +0 -0
  133. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/graph/__init__.py +0 -0
  134. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/graph/build.py +0 -0
  135. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/graph/cell_types.py +0 -0
  136. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/graph/ingest.py +0 -0
  137. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/graph/query.py +0 -0
  138. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/graph/schema.py +0 -0
  139. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/mech/__init__.py +0 -0
  140. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/mech/classify_atlas.py +0 -0
  141. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/mech/whitelist.py +0 -0
  142. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/monitor/__init__.py +0 -0
  143. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/monitor/europepmc.py +0 -0
  144. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/monitor/run.py +0 -0
  145. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/monitor/triage.py +0 -0
  146. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/__init__.py +0 -0
  147. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/cache.py +0 -0
  148. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/energetics.py +0 -0
  149. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/genome.py +0 -0
  150. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/protein_design.py +0 -0
  151. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/rna.py +0 -0
  152. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/schema.py +0 -0
  153. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/oracles/structure.py +0 -0
  154. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/__init__.py +0 -0
  155. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/cargo.py +0 -0
  156. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/cargo_polish.py +0 -0
  157. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/delivery.py +0 -0
  158. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/delivery_constraints.py +0 -0
  159. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/delivery_vehicles.py +0 -0
  160. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/multiplex.py +0 -0
  161. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/optimize.py +0 -0
  162. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/pipeline.py +0 -0
  163. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/report.py +0 -0
  164. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/router.py +0 -0
  165. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/planner/target_site.py +0 -0
  166. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rag/__init__.py +0 -0
  167. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rag/index.py +0 -0
  168. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rag/llm.py +0 -0
  169. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rag/qa.py +0 -0
  170. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rules/__init__.py +0 -0
  171. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rules/evaluators.py +0 -0
  172. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rules/loader.py +0 -0
  173. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rules/schema.py +0 -0
  174. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/rules/solver.py +0 -0
  175. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/score/__init__.py +0 -0
  176. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/score/recalibrate.py +0 -0
  177. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/score/therapeutic.py +0 -0
  178. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/server/__init__.py +0 -0
  179. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/server/api.py +0 -0
  180. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/ui/__init__.py +0 -0
  181. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/ui/app.py +0 -0
  182. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/__init__.py +0 -0
  183. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/adapt_demo.py +0 -0
  184. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/agent_eval.py +0 -0
  185. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  186. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
  187. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/bench_graph_tasks.py +0 -0
  188. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/bench_rule_tasks.py +0 -0
  189. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/bench_trust_tasks.py +0 -0
  190. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  191. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  192. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/cargo_directionality.py +0 -0
  193. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/durability_baselines.py +0 -0
  194. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/forward_hypotheses.py +0 -0
  195. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/guide_qc_demo.py +0 -0
  196. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/intent_specification.py +0 -0
  197. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  198. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  199. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/outcome_calibration.py +0 -0
  200. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/paper3_benchmark.py +0 -0
  201. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/paper4_real_validation.py +0 -0
  202. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/paper4_validation.py +0 -0
  203. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/selective_prediction.py +0 -0
  204. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/seq_vs_measured.py +0 -0
  205. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/target_site_controls.py +0 -0
  206. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/uncertainty_eval.py +0 -0
  207. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/ungrounded_baseline.py +0 -0
  208. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/within_locus_ranking.py +0 -0
  209. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/validate/writer_recovery.py +0 -0
  210. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/verify/__init__.py +0 -0
  211. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/verify/schema.py +0 -0
  212. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/verify/service.py +0 -0
  213. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/__init__.py +0 -0
  214. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/chromatin_seq.py +0 -0
  215. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/durability.py +0 -0
  216. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/export_tracks.py +0 -0
  217. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/features.py +0 -0
  218. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/gsh_baseline.py +0 -0
  219. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/mesh_features.py +0 -0
  220. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/ood.py +0 -0
  221. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/providers.py +0 -0
  222. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/safety.py +0 -0
  223. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/structure3d.py +0 -0
  224. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/uncertainty.py +0 -0
  225. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack/wgenome/writability.py +0 -0
  226. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack.egg-info/dependency_links.txt +0 -0
  227. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack.egg-info/entry_points.txt +0 -0
  228. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack.egg-info/requires.txt +0 -0
  229. {pen_stack-5.1.0 → pen_stack-5.2.0}/pen_stack.egg-info/top_level.txt +0 -0
  230. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_phase0.json +0 -0
  231. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  232. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_phase2.json +0 -0
  233. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_phase3.json +0 -0
  234. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_a.json +0 -0
  235. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  236. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_b.json +0 -0
  237. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  238. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  239. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  240. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  241. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_c.json +0 -0
  242. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  243. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_cite.json +0 -0
  244. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_crit.json +0 -0
  245. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  246. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_d.json +0 -0
  247. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_e.json +0 -0
  248. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_env.json +0 -0
  249. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  250. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_f.json +0 -0
  251. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_g.json +0 -0
  252. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  253. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_h.json +0 -0
  254. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_immune.json +0 -0
  255. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  256. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  257. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_o.json +0 -0
  258. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_plan.json +0 -0
  259. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_r.json +0 -0
  260. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_route.json +0 -0
  261. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  262. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_v.json +0 -0
  263. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  264. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/paper1.yaml +0 -0
  265. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/paper2.yaml +0 -0
  266. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/paper3.yaml +0 -0
  267. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/paper4.yaml +0 -0
  268. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/phase0.yaml +0 -0
  269. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_a.yaml +0 -0
  270. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_atlas.yaml +0 -0
  271. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_b.yaml +0 -0
  272. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_ba.yaml +0 -0
  273. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_ba_v33.yaml +0 -0
  274. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_ba_v45.yaml +0 -0
  275. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_bench.yaml +0 -0
  276. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_c.yaml +0 -0
  277. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_cal.yaml +0 -0
  278. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_cite.yaml +0 -0
  279. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_crit.yaml +0 -0
  280. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_ct.yaml +0 -0
  281. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_d.yaml +0 -0
  282. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_e.yaml +0 -0
  283. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_env.yaml +0 -0
  284. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_ep.yaml +0 -0
  285. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_f.yaml +0 -0
  286. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_g.yaml +0 -0
  287. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_graph.yaml +0 -0
  288. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_h.yaml +0 -0
  289. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_immune.yaml +0 -0
  290. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_mc.yaml +0 -0
  291. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_mon.yaml +0 -0
  292. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_o.yaml +0 -0
  293. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_plan.yaml +0 -0
  294. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_r.yaml +0 -0
  295. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_route.yaml +0 -0
  296. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_uq.yaml +0 -0
  297. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_v.yaml +0 -0
  298. {pen_stack-5.1.0 → pen_stack-5.2.0}/prereg/ws_wv.yaml +0 -0
  299. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p1_build_atlas.py +0 -0
  300. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p1_build_durability.py +0 -0
  301. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p1_export_tracks.py +0 -0
  302. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p1_safety_concordance.py +0 -0
  303. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p1_train_safety.py +0 -0
  304. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p1_validation_report.py +0 -0
  305. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p2_build_atlas.py +0 -0
  306. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p3_benchmark_report.py +0 -0
  307. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/p4_genome_scan.py +0 -0
  308. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/ws_b_report.py +0 -0
  309. {pen_stack-5.1.0 → pen_stack-5.2.0}/scripts/ws_c_report.py +0 -0
  310. {pen_stack-5.1.0 → pen_stack-5.2.0}/setup.cfg +0 -0
@@ -3,6 +3,39 @@
3
3
  All notable changes to PEN-STACK are documented here. This file follows
4
4
  [Keep a Changelog](https://keepachangelog.com/) and the program's phase structure.
5
5
 
6
+ ## [5.2.0] - 2026-06-10 - v5.2 release: Computed genotoxicity oracle (data, not a documented tier)
7
+
8
+ The v5.1 genotoxicity axis was a documented ordinal tier; for **integrating** vectors that signal is in fact
9
+ computable from data the stack already holds. v5.2 adds a **computed genotoxicity oracle** — the observed
10
+ enrichment of a vector class's integration sites near COSMIC oncogenes — answering through the v4.0
11
+ OracleResult contract. Workstream WS-GENOTOX, SHA-locked.
12
+
13
+ ### Added
14
+ - **WS-GENOTOX build** — `scripts/p52_build_genotox_oracle.py` (runs on the VM where the data lives) computes,
15
+ per integrating vector class, `P(integration site within 50 kb of a COSMIC Cancer-Gene-Census oncogene)` and
16
+ its enrichment over genome background, from **VISDB** per-virus catalogues × the Phase-1 oncogene annotation
17
+ (**COSMIC CGC v104**). Emits the small, auditable, committed summary `configs/genotoxicity_oracle.yaml` (raw
18
+ catalogues stay on the VM; only the statistics ship → CI-safe).
19
+ - **WS-GENOTOX oracle** — `pen_stack/planner/genotoxicity_oracle.py`: `genotoxicity_oracle(vehicle)` returns an
20
+ `OracleResult` (`output_kind="baseline"`) with `genotox_score = min(1, 1/enrichment)`, native uncertainty
21
+ (CI on the observed fraction), the `delivery_genotoxicity` scope card, and `extrapolating` for small-n
22
+ classes. Non-integrating vehicles → 1.0 by mechanism; no computed class → **abstains** (never fabricates).
23
+ - **Wired into the v5.1 balance** — `safety_efficacy_profile()` now **prefers the computed genotox_score** for
24
+ integrating vectors and falls back to the documented tier otherwise (`genotox_source` records which).
25
+ - **Result (from data):** lentiviral (HIV) integration is **2.08×** enriched near oncogenes (n=88,743, robust)
26
+ vs **5.65×** for gammaretroviral (MLV, the LMO2/SCID-X1 comparator, small-n flagged) — reproducing the
27
+ lentivirus-safer-than-gammaretrovirus ordering **from VISDB×COSMIC**, and the computed lentivirus score
28
+ (0.48) **validates** the v5.1 documented "moderate" tier (0.5). `prereg/ws_genotox.yaml`.
29
+
30
+ ### Changed
31
+ - Version 5.1.0 -> 5.2.0 (minor — additive computed oracle); `cite.curated_dois()` ingests the genotox
32
+ provenance DOIs (VISDB 10.1093/nar/gkz867, COSMIC CGC 10.1038/s41568-018-0060-1, HIV/MLV integration biology).
33
+
34
+ ### Honesty invariant (unchanged)
35
+ - This is a **relative integration-preference** signal. The in-vivo clonal-expansion / leukemogenesis OUTCOME
36
+ in a patient is **not** modelled and stays a known-unknown (`delivery_genotoxicity` scope card); the immune
37
+ MAGNITUDE likewise stays `in_vivo_immunogenicity`. No magnitude is predicted.
38
+
6
39
  ## [5.1.0] - 2026-06-10 - v5.1 release: Delivery immunology (the safety↔efficacy balance)
7
40
 
8
41
  The delivery palette gains a **documented, cited, qualitative immune + safety + efficacy profile** per vehicle,
@@ -1,7 +1,7 @@
1
1
  cff-version: 1.2.0
2
2
  message: "If you use PEN-STACK, please cite it as below."
3
3
  title: "PEN-STACK: open infrastructure for genome writing"
4
- version: 5.1.0
4
+ version: 5.2.0
5
5
  date-released: 2026-06-10
6
6
  authors:
7
7
  - family-names: "Mahaboob Ali"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 5.1.0
3
+ Version: 5.2.0
4
4
  Summary: Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner.
5
5
  Author-email: Anees Ahmed Mahaboob Ali <ahmedaneesm@gmail.com>
6
6
  License: MIT
@@ -91,7 +91,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
91
91
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
92
92
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
93
93
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
94
- [![Version](https://img.shields.io/badge/version-5.1.0-blue.svg)](CHANGELOG.md)
94
+ [![Version](https://img.shields.io/badge/version-5.2.0-blue.svg)](CHANGELOG.md)
95
95
  [![Tests](https://img.shields.io/badge/tests-240%20passing-success.svg)](tests/)
96
96
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
97
97
  [![Runtime: Docker](https://img.shields.io/badge/runtime-docker-2496ED.svg)](docker/)
@@ -135,6 +135,22 @@ Two questions gate every genome-writing project, and before PEN-STACK no resourc
135
135
  Everything is built on bulk-downloadable public data, runs on a single GPU, and is validated **blind** against
136
136
  a pre-registered, honest baseline before release.
137
137
 
138
+ ## What is new in v5.2 — Computed genotoxicity oracle (data, not a documented tier)
139
+
140
+ v5.1 scored genotoxicity as a documented `low/moderate/high` tier. v5.2 makes it **computed from data** for
141
+ integrating vectors: the observed enrichment of a vector class's integration sites near COSMIC oncogenes
142
+ (VISDB integration catalogues × the Phase-1 COSMIC-CGC oncogene annotation), surfaced through the v4.0
143
+ `OracleResult` contract. The in-vivo clonal / leukemogenesis **outcome** stays a known-unknown — this is a
144
+ relative integration-*preference* signal, not a per-patient oncogenesis probability.
145
+
146
+ | Workstream | What it adds | Result |
147
+ |---|---|---|
148
+ | **GENOTOX build** | `scripts/p52_build_genotox_oracle.py` → committed `configs/genotoxicity_oracle.yaml` | per integrating class: `P(site within 50 kb of a COSMIC oncogene)`, enrichment vs background, CI, n — from VISDB × COSMIC CGC v104 (raw data stays on the VM; only the auditable summary ships) |
149
+ | **GENOTOX oracle** | `planner/genotoxicity_oracle.py` (`OracleResult`, `output_kind="baseline"`) | `genotox_score = min(1, 1/enrichment)`; non-integrating → 1.0 by mechanism; **abstains** when it has no computed class (never fabricates); small-n classes flagged `extrapolating` |
150
+ | **wired into v5.1 balance** | `safety_efficacy_profile()` prefers computed genotox, falls back to the documented tier | **lentiviral 2.08×** vs **gammaretroviral 5.65×** oncogene-proximity enrichment — reproduces the lentivirus-safer-than-gammaretrovirus ordering **from data**; computed LV score (0.48) **validates** the v5.1 documented tier (0.5) |
151
+
152
+ See `prereg/ws_genotox.yaml` and the `delivery_genotoxicity` scope card.
153
+
138
154
  ## What is new in v5.1 — Delivery immunology (the safety↔efficacy balance)
139
155
 
140
156
  v5.1 makes the delivery palette's **safety↔efficacy tradeoff legible and user-weightable**. Every vehicle now
@@ -16,7 +16,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
16
16
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
17
17
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
18
18
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
19
- [![Version](https://img.shields.io/badge/version-5.1.0-blue.svg)](CHANGELOG.md)
19
+ [![Version](https://img.shields.io/badge/version-5.2.0-blue.svg)](CHANGELOG.md)
20
20
  [![Tests](https://img.shields.io/badge/tests-240%20passing-success.svg)](tests/)
21
21
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
22
22
  [![Runtime: Docker](https://img.shields.io/badge/runtime-docker-2496ED.svg)](docker/)
@@ -60,6 +60,22 @@ Two questions gate every genome-writing project, and before PEN-STACK no resourc
60
60
  Everything is built on bulk-downloadable public data, runs on a single GPU, and is validated **blind** against
61
61
  a pre-registered, honest baseline before release.
62
62
 
63
+ ## What is new in v5.2 — Computed genotoxicity oracle (data, not a documented tier)
64
+
65
+ v5.1 scored genotoxicity as a documented `low/moderate/high` tier. v5.2 makes it **computed from data** for
66
+ integrating vectors: the observed enrichment of a vector class's integration sites near COSMIC oncogenes
67
+ (VISDB integration catalogues × the Phase-1 COSMIC-CGC oncogene annotation), surfaced through the v4.0
68
+ `OracleResult` contract. The in-vivo clonal / leukemogenesis **outcome** stays a known-unknown — this is a
69
+ relative integration-*preference* signal, not a per-patient oncogenesis probability.
70
+
71
+ | Workstream | What it adds | Result |
72
+ |---|---|---|
73
+ | **GENOTOX build** | `scripts/p52_build_genotox_oracle.py` → committed `configs/genotoxicity_oracle.yaml` | per integrating class: `P(site within 50 kb of a COSMIC oncogene)`, enrichment vs background, CI, n — from VISDB × COSMIC CGC v104 (raw data stays on the VM; only the auditable summary ships) |
74
+ | **GENOTOX oracle** | `planner/genotoxicity_oracle.py` (`OracleResult`, `output_kind="baseline"`) | `genotox_score = min(1, 1/enrichment)`; non-integrating → 1.0 by mechanism; **abstains** when it has no computed class (never fabricates); small-n classes flagged `extrapolating` |
75
+ | **wired into v5.1 balance** | `safety_efficacy_profile()` prefers computed genotox, falls back to the documented tier | **lentiviral 2.08×** vs **gammaretroviral 5.65×** oncogene-proximity enrichment — reproduces the lentivirus-safer-than-gammaretrovirus ordering **from data**; computed LV score (0.48) **validates** the v5.1 documented tier (0.5) |
76
+
77
+ See `prereg/ws_genotox.yaml` and the `delivery_genotoxicity` scope card.
78
+
63
79
  ## What is new in v5.1 — Delivery immunology (the safety↔efficacy balance)
64
80
 
65
81
  v5.1 makes the delivery palette's **safety↔efficacy tradeoff legible and user-weightable**. Every vehicle now
@@ -0,0 +1,48 @@
1
+ version: '1.0'
2
+ built: '2026-06-10'
3
+ description: 'computed integration-site genotoxicity oracle: per vector class, the
4
+ observed enrichment of integration sites within window_bp of a COSMIC oncogene vs
5
+ genome background. genotox_score = min(1, 1/enrichment). In-vivo clonal outcome
6
+ is NOT modelled (stays a known-unknown).'
7
+ window_bp: 50000
8
+ genome_background_frac_oncogene_50kb: 0.02211
9
+ inputs:
10
+ visdb: VISDB per-virus hg38 catalogues
11
+ oncogenes: COSMIC CGC v104 (safety_annot)
12
+ provenance_dois:
13
+ - 10.1093/nar/gkz867
14
+ - 10.1038/s41568-018-0060-1
15
+ - 10.1016/S0092-8674(02)00864-4
16
+ - 10.1126/science.1083413
17
+ robust_min_n: 1000
18
+ classes:
19
+ lentiviral:
20
+ virus: HIV
21
+ n_sites: 88743
22
+ frac_oncogene_50kb: 0.04602
23
+ ci95: 0.00138
24
+ enrichment: 2.081
25
+ frac_genotoxic_cis: 0.000293
26
+ median_dist_oncogene: 2339098
27
+ robust: true
28
+ deltaretroviral:
29
+ virus: HTLV
30
+ n_sites: 51508
31
+ frac_oncogene_50kb: 0.02648
32
+ ci95: 0.00139
33
+ enrichment: 1.198
34
+ frac_genotoxic_cis: 0.000369
35
+ median_dist_oncogene: 3766674
36
+ robust: true
37
+ gammaretroviral:
38
+ virus: MLV
39
+ n_sites: 32
40
+ frac_oncogene_50kb: 0.125
41
+ ci95: 0.11459
42
+ enrichment: 5.653
43
+ frac_genotoxic_cis: 0.0
44
+ median_dist_oncogene: 2871705
45
+ robust: false
46
+ vehicle_class:
47
+ lentiviral:
48
+ - lentivirus
@@ -112,3 +112,16 @@ oracles:
112
112
  not_valid_for: "absolute off-target rates; non-bridge writers; a non-recombining background"
113
113
  generalizes_to_unseen_loci: false
114
114
  license: "open (this work)"
115
+
116
+ delivery_genotoxicity: # v5.2 WS-GENOTOX: computed integration-site oncogene-proximity
117
+ family: genome
118
+ version: "visdb+cgc_v104-2026"
119
+ output_kind: baseline # an observed-data comparator (integration catalogues), not generative
120
+ valid_for: "RELATIVE genotoxicity ordering of INTEGRATING vector classes via the observed enrichment of
121
+ integration sites near COSMIC oncogenes (lentiviral vs gammaretroviral, from VISDB x CGC); reproduces the
122
+ lentivirus-safer-than-gammaretrovirus ordering from data"
123
+ not_valid_for: "the IN-VIVO clonal-expansion / leukemogenesis OUTCOME in a patient (a known-unknown); an
124
+ absolute per-insertion oncogenesis probability; non-integrating vectors (no insertional mechanism);
125
+ classes with too few catalogued sites (flagged extrapolating)"
126
+ generalizes_to_unseen_loci: false
127
+ license: "open (this work; VISDB 10.1093/nar/gkz867, COSMIC CGC 10.1038/s41568-018-0060-1)"
@@ -1,2 +1,2 @@
1
1
  """PEN-STACK v3.0 - open infrastructure for genome writing."""
2
- __version__ = "5.1.0"
2
+ __version__ = "5.2.0"
@@ -29,6 +29,12 @@ def curated_dois() -> frozenset[str]:
29
29
  for v in veh.values():
30
30
  dois.update(v.get("dois", []) or [])
31
31
  dois.update((v.get("immune_safety") or {}).get("immune_dois", []) or []) # v5.1 immune priors
32
+ # v5.2 computed-genotoxicity oracle provenance (VISDB, COSMIC CGC, integration-biology refs)
33
+ try:
34
+ gt = yaml.safe_load(resource("configs/genotoxicity_oracle.yaml").read_text(encoding="utf-8"))
35
+ dois.update(gt.get("provenance_dois", []) or [])
36
+ except FileNotFoundError:
37
+ pass
32
38
  gsh = yaml.safe_load(resource("configs/gsh_validated_heldout.yaml").read_text(encoding="utf-8"))["gsh"]
33
39
  for g in gsh:
34
40
  if g.get("doi"):
@@ -60,8 +60,16 @@ def safety_efficacy_profile(name: str) -> dict | None:
60
60
  imm = rec.get("immune_safety") or {}
61
61
  immune_present = [t for ax in _IMMUNE_AXES if (t := _tier(imm.get(ax))) is not None]
62
62
  immune_score = (1.0 - (sum(immune_present) / len(immune_present)) / 2.0) if immune_present else None
63
- gtox = _tier(imm.get(_GENOTOX_AXIS))
64
- genotox_score = (1.0 - gtox / 2.0) if gtox is not None else None
63
+ # genotoxicity: prefer the COMPUTED oracle (v5.2 WS-GENOTOX: integration-site x COSMIC-oncogene
64
+ # enrichment, from VISDB) for integrating vectors; fall back to the documented ordinal tier when the
65
+ # oracle abstains. No number is fabricated either way.
66
+ from pen_stack.planner.genotoxicity_oracle import computed_genotox_score
67
+ computed, gtox_oracle = computed_genotox_score(name)
68
+ gtox_tier = _tier(imm.get(_GENOTOX_AXIS))
69
+ documented_genotox = (1.0 - gtox_tier / 2.0) if gtox_tier is not None else None
70
+ genotox_score = computed if computed is not None else documented_genotox
71
+ genotox_source = "computed" if computed is not None else ("documented" if documented_genotox is not None
72
+ else None)
65
73
  sub = [s for s in (immune_score, genotox_score) if s is not None]
66
74
  safety_score = min(sub) if sub else None # worst-axis; abstain if neither documented
67
75
  eff = _tier(imm.get("efficacy"))
@@ -72,6 +80,8 @@ def safety_efficacy_profile(name: str) -> dict | None:
72
80
  "tiers": {ax: imm.get(ax) for ax in (*_IMMUNE_AXES, _GENOTOX_AXIS)} | {"efficacy": imm.get("efficacy")},
73
81
  "immune_score": _r(immune_score),
74
82
  "genotox_score": _r(genotox_score),
83
+ "genotox_source": genotox_source, # "computed" (VISDBxCOSMIC oracle) | "documented" (ordinal tier)
84
+ "genotox_provenance": (gtox_oracle.note if genotox_source == "computed" else None),
75
85
  "safety_score": _r(safety_score),
76
86
  "efficacy_score": _r(efficacy_score),
77
87
  "re_dosable": imm.get("re_dosable", rec.get("re_dosable")),
@@ -0,0 +1,112 @@
1
+ """Computed genotoxicity oracle for integrating delivery vectors (v5.2, WS-GENOTOX).
2
+
3
+ Replaces the hard-coded `genotoxicity` ordinal tier (v5.1, documented prior) with a DATA-COMPUTED signal for
4
+ INTEGRATING vehicles: the observed enrichment of a vector class's integration sites near COSMIC Cancer-Gene-
5
+ Census oncogenes, from VISDB integration catalogues x the Phase-1 oncogene annotation (configs/
6
+ genotoxicity_oracle.yaml, built by scripts/p52_build_genotox_oracle.py on the VM where the data lives).
7
+
8
+ genotox_score = min(1, 1 / enrichment) # 1 = safest; episomal/non-targeting ~ 1.0
9
+
10
+ This reproduces the lentivirus-safer-than-gammaretrovirus ordering FROM DATA (lentiviral ~2x oncogene-proximity
11
+ enrichment vs gammaretroviral ~5-6x — the LMO2 / SCID-X1 pattern), instead of asserting it. It answers through
12
+ the v4.0 OracleResult contract (value + provenance + native_uncertainty + scope_card + output_kind) and is an
13
+ `output_kind="baseline"` observed-data comparator, NOT a generative claim.
14
+
15
+ HONESTY: this is a RELATIVE, integration-PREFERENCE signal. The in-vivo clonal-expansion / leukemogenesis
16
+ OUTCOME in a patient is NOT modelled and stays a known-unknown; a class with too few catalogued sites is
17
+ flagged `extrapolating`; non-integrating vehicles have no insertional mechanism (score 1.0 by mechanism).
18
+ """
19
+ from __future__ import annotations
20
+
21
+ from functools import lru_cache
22
+
23
+ import yaml
24
+
25
+ from pen_stack._resources import resource
26
+ from pen_stack.oracles.schema import OracleResult, Provenance
27
+ from pen_stack.planner.delivery_vehicles import vehicle
28
+
29
+ _SCOPE_CARD = "delivery_genotoxicity"
30
+
31
+
32
+ @lru_cache(maxsize=1)
33
+ def _artifact() -> dict:
34
+ return yaml.safe_load(resource("configs/genotoxicity_oracle.yaml").read_text(encoding="utf-8"))
35
+
36
+
37
+ @lru_cache(maxsize=1)
38
+ def _vehicle_to_class() -> dict:
39
+ art = _artifact()
40
+ out: dict[str, str] = {}
41
+ for cls, vehs in (art.get("vehicle_class") or {}).items():
42
+ for v in vehs or []:
43
+ out[v] = cls
44
+ return out
45
+
46
+
47
+ def _prov(source: str, **extra) -> Provenance:
48
+ art = _artifact()
49
+ return Provenance(model="visdb_integration_x_cosmic_cgc", version=str(art.get("version", "1.0")),
50
+ source=source, extra={"built": art.get("built"),
51
+ "provenance_dois": art.get("provenance_dois", []), **extra})
52
+
53
+
54
+ def genotoxicity_oracle(vehicle_name: str) -> OracleResult:
55
+ """Computed genotoxicity for a delivery vehicle, as an OracleResult (v4.0 contract).
56
+
57
+ - non-integrating vehicle -> genotox_score 1.0 by mechanism (episomal/transient; no insertional risk).
58
+ - integrating + computed class -> data-derived score from VISDB x COSMIC; small-n class -> extrapolating.
59
+ - integrating but no computed class / unknown vehicle -> available=False (caller falls back to the
60
+ documented `immune_safety.genotoxicity` tier; no number is fabricated)."""
61
+ rec = vehicle(vehicle_name)
62
+ if rec is None:
63
+ return OracleResult(oracle="genome", value=None, provenance=_prov("cache"),
64
+ scope_card=_SCOPE_CARD, in_scope=False, available=False, output_kind="baseline",
65
+ note=f"unknown vehicle {vehicle_name!r}")
66
+
67
+ if not rec.get("integrating"):
68
+ return OracleResult(
69
+ oracle="genome",
70
+ value={"genotox_score": 1.0, "enrichment": None, "mechanism": "non-integrating"},
71
+ provenance=_prov("cache"), native_uncertainty=0.0, scope_card=_SCOPE_CARD, in_scope=True,
72
+ extrapolating=False, output_kind="baseline", available=True,
73
+ note="episomal/transient vector: no integration -> no insertional-mutagenesis mechanism (score 1.0).")
74
+
75
+ cls = _vehicle_to_class().get(vehicle_name)
76
+ classes = _artifact().get("classes") or {}
77
+ if not cls or cls not in classes:
78
+ return OracleResult(oracle="genome", value=None, provenance=_prov("cache"),
79
+ scope_card=_SCOPE_CARD, in_scope=False, available=False, output_kind="baseline",
80
+ note=f"integrating vehicle {vehicle_name!r} has no computed class; "
81
+ "fall back to the documented genotoxicity tier.")
82
+
83
+ rc = classes[cls]
84
+ enrich = rc.get("enrichment")
85
+ frac, ci95, n = rc.get("frac_oncogene_50kb"), rc.get("ci95"), rc.get("n_sites")
86
+ score = min(1.0, 1.0 / enrich) if enrich else None
87
+ robust = bool(rc.get("robust"))
88
+ # native uncertainty = relative CI on the observed oncogene-proximity fraction (coefficient of variation)
89
+ nu = round(ci95 / frac, 4) if (ci95 and frac) else None
90
+ return OracleResult(
91
+ oracle="genome",
92
+ value={"genotox_score": round(score, 3) if score is not None else None,
93
+ "enrichment": enrich, "frac_oncogene_50kb": frac, "ci95": ci95, "n_sites": n,
94
+ "frac_genotoxic_cis": rc.get("frac_genotoxic_cis"), "vector_class": cls,
95
+ "background_frac": _artifact().get("genome_background_frac_oncogene_50kb")},
96
+ provenance=_prov("cache", virus=rc.get("virus"), vector_class=cls),
97
+ native_uncertainty=nu, scope_card=_SCOPE_CARD, in_scope=True,
98
+ extrapolating=not robust, output_kind="baseline", available=True,
99
+ note=(f"{cls} integration is {enrich}x enriched within "
100
+ f"{_artifact().get('window_bp')} bp of a COSMIC oncogene vs background "
101
+ f"({frac:.3%}, n={n}); genotox_score=min(1,1/enrichment)."
102
+ + ("" if robust else " SMALL-N class: directional only (extrapolating).")
103
+ + " In-vivo clonal outcome is a known-unknown (not modelled).")
104
+ )
105
+
106
+
107
+ def computed_genotox_score(vehicle_name: str) -> tuple[float | None, OracleResult]:
108
+ """Convenience: (genotox_score or None, full OracleResult). None when the oracle abstains (caller then
109
+ uses the documented tier). Never fabricates a number."""
110
+ r = genotoxicity_oracle(vehicle_name)
111
+ val = (r.value or {}).get("genotox_score") if (r.available and r.value) else None
112
+ return val, r
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 5.1.0
3
+ Version: 5.2.0
4
4
  Summary: Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner.
5
5
  Author-email: Anees Ahmed Mahaboob Ali <ahmedaneesm@gmail.com>
6
6
  License: MIT
@@ -91,7 +91,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
91
91
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
92
92
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
93
93
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
94
- [![Version](https://img.shields.io/badge/version-5.1.0-blue.svg)](CHANGELOG.md)
94
+ [![Version](https://img.shields.io/badge/version-5.2.0-blue.svg)](CHANGELOG.md)
95
95
  [![Tests](https://img.shields.io/badge/tests-240%20passing-success.svg)](tests/)
96
96
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
97
97
  [![Runtime: Docker](https://img.shields.io/badge/runtime-docker-2496ED.svg)](docker/)
@@ -135,6 +135,22 @@ Two questions gate every genome-writing project, and before PEN-STACK no resourc
135
135
  Everything is built on bulk-downloadable public data, runs on a single GPU, and is validated **blind** against
136
136
  a pre-registered, honest baseline before release.
137
137
 
138
+ ## What is new in v5.2 — Computed genotoxicity oracle (data, not a documented tier)
139
+
140
+ v5.1 scored genotoxicity as a documented `low/moderate/high` tier. v5.2 makes it **computed from data** for
141
+ integrating vectors: the observed enrichment of a vector class's integration sites near COSMIC oncogenes
142
+ (VISDB integration catalogues × the Phase-1 COSMIC-CGC oncogene annotation), surfaced through the v4.0
143
+ `OracleResult` contract. The in-vivo clonal / leukemogenesis **outcome** stays a known-unknown — this is a
144
+ relative integration-*preference* signal, not a per-patient oncogenesis probability.
145
+
146
+ | Workstream | What it adds | Result |
147
+ |---|---|---|
148
+ | **GENOTOX build** | `scripts/p52_build_genotox_oracle.py` → committed `configs/genotoxicity_oracle.yaml` | per integrating class: `P(site within 50 kb of a COSMIC oncogene)`, enrichment vs background, CI, n — from VISDB × COSMIC CGC v104 (raw data stays on the VM; only the auditable summary ships) |
149
+ | **GENOTOX oracle** | `planner/genotoxicity_oracle.py` (`OracleResult`, `output_kind="baseline"`) | `genotox_score = min(1, 1/enrichment)`; non-integrating → 1.0 by mechanism; **abstains** when it has no computed class (never fabricates); small-n classes flagged `extrapolating` |
150
+ | **wired into v5.1 balance** | `safety_efficacy_profile()` prefers computed genotox, falls back to the documented tier | **lentiviral 2.08×** vs **gammaretroviral 5.65×** oncogene-proximity enrichment — reproduces the lentivirus-safer-than-gammaretrovirus ordering **from data**; computed LV score (0.48) **validates** the v5.1 documented tier (0.5) |
151
+
152
+ See `prereg/ws_genotox.yaml` and the `delivery_genotoxicity` scope card.
153
+
138
154
  ## What is new in v5.1 — Delivery immunology (the safety↔efficacy balance)
139
155
 
140
156
  v5.1 makes the delivery palette's **safety↔efficacy tradeoff legible and user-weightable**. Every vehicle now
@@ -19,6 +19,7 @@ configs/delivery_constraints.yaml
19
19
  configs/delivery_rules.yaml
20
20
  configs/delivery_vehicles.yaml
21
21
  configs/gates_v3.yaml
22
+ configs/genotoxicity_oracle.yaml
22
23
  configs/gsh_validated_heldout.yaml
23
24
  configs/intent_weights.yaml
24
25
  configs/known_unknowns.yaml
@@ -155,6 +156,7 @@ pen_stack/planner/delivery.py
155
156
  pen_stack/planner/delivery_constraints.py
156
157
  pen_stack/planner/delivery_immunology.py
157
158
  pen_stack/planner/delivery_vehicles.py
159
+ pen_stack/planner/genotoxicity_oracle.py
158
160
  pen_stack/planner/multiplex.py
159
161
  pen_stack/planner/optimize.py
160
162
  pen_stack/planner/pipeline.py
@@ -243,6 +245,7 @@ prereg/SHA256_LOCK_ws_env.json
243
245
  prereg/SHA256_LOCK_ws_ep.json
244
246
  prereg/SHA256_LOCK_ws_f.json
245
247
  prereg/SHA256_LOCK_ws_g.json
248
+ prereg/SHA256_LOCK_ws_genotox.json
246
249
  prereg/SHA256_LOCK_ws_graph.json
247
250
  prereg/SHA256_LOCK_ws_h.json
248
251
  prereg/SHA256_LOCK_ws_immune.json
@@ -278,6 +281,7 @@ prereg/ws_env.yaml
278
281
  prereg/ws_ep.yaml
279
282
  prereg/ws_f.yaml
280
283
  prereg/ws_g.yaml
284
+ prereg/ws_genotox.yaml
281
285
  prereg/ws_graph.yaml
282
286
  prereg/ws_h.yaml
283
287
  prereg/ws_immune.yaml
@@ -299,5 +303,6 @@ scripts/p1_validation_report.py
299
303
  scripts/p2_build_atlas.py
300
304
  scripts/p3_benchmark_report.py
301
305
  scripts/p4_genome_scan.py
306
+ scripts/p52_build_genotox_oracle.py
302
307
  scripts/ws_b_report.py
303
308
  scripts/ws_c_report.py
@@ -0,0 +1,8 @@
1
+ {
2
+ "cycle": "v5.2",
3
+ "workstream": "WS-GENOTOX",
4
+ "prepared": "2026-06-10",
5
+ "sha256": {
6
+ "prereg/ws_genotox.yaml": "4cd8534034fd42986708345eb911bdf62200d0a9c528f80378b29920b4ce434b"
7
+ }
8
+ }
@@ -0,0 +1,41 @@
1
+ # PEN-STACK v5.2 - WS-GENOTOX (computed integration-site genotoxicity oracle). Directional.
2
+ cycle: "v5.2"
3
+ workstream: "WS-GENOTOX"
4
+ prepared: "2026-06-10"
5
+
6
+ motivation: >
7
+ Follow-up to v5.1 (user): "do we have the option of getting an actual CALCULATION from the datasets /
8
+ databases, like we used to with NetMHC / ENCODE / COSMIC?" The v5.1 genotoxicity axis was a documented
9
+ ordinal tier; for INTEGRATING vectors the signal is in fact computable from data the stack already holds.
10
+
11
+ deliverable: >
12
+ scripts/p52_build_genotox_oracle.py (VM build) -> configs/genotoxicity_oracle.yaml (committed summary);
13
+ pen_stack/planner/genotoxicity_oracle.py (OracleResult); wired into delivery_immunology.safety_efficacy_profile
14
+ (computed genotox_score preferred, documented tier fallback); scope card `delivery_genotoxicity`.
15
+
16
+ method: >
17
+ For each integrating vector CLASS, compute from real data the observed enrichment of its integration sites
18
+ near COSMIC Cancer-Gene-Census oncogenes: risk = P(site within 50 kb of an oncogene) over the class's VISDB
19
+ catalogue; enrichment = risk / genome_background_frac; genotox_score = min(1, 1/enrichment). HIV ~ lentiviral
20
+ (-> lentivirus vehicle); MLV ~ gammaretroviral (the LMO2 / SCID-X1 comparator, NOT in the palette). Inputs:
21
+ VISDB per-virus hg38 catalogues [10.1093/nar/gkz867] x the Phase-1 oncogene annotation from COSMIC CGC v104
22
+ [10.1038/s41568-018-0060-1]; integration biology [HIV 10.1016/S0092-8674(02)00864-4; MLV 10.1126/science.1083413].
23
+ Answers through the v4.0 OracleResult contract (output_kind="baseline"); raw catalogues stay on the VM, only
24
+ the auditable summary ships (CI-safe).
25
+
26
+ acceptance: >
27
+ (1) the integrating in-palette vehicle (lentivirus) gets a COMPUTED genotox_score from VISDB x COSMIC, with a
28
+ native uncertainty and an in-scope OracleResult; (2) the computation reproduces the lentivirus-safer-than-
29
+ gammaretrovirus ordering FROM DATA (lentiviral enrichment < gammaretroviral enrichment); (3) the computed
30
+ lentivirus score VALIDATES the v5.1 documented "moderate" tier (within +/-0.1 of 0.5), i.e. the prior is
31
+ confirmed not contradicted; (4) non-integrating vehicles are 1.0 by mechanism; (5) the oracle ABSTAINS
32
+ (available=False, no number) when it has no computed class - never fabricates; (6) small-n classes are flagged
33
+ extrapolating; (7) provenance DOIs resolve in the curated set.
34
+
35
+ honesty_invariant: >
36
+ This is a RELATIVE, integration-PREFERENCE signal. The in-vivo clonal-expansion / leukemogenesis OUTCOME in a
37
+ patient is NOT modelled and stays a known-unknown (scope card `delivery_genotoxicity.not_valid_for`); the
38
+ immune MAGNITUDE likewise stays `in_vivo_immunogenicity`. No magnitude is predicted.
39
+
40
+ gate: "lentivirus genotox computed from VISDBxCOSMIC; LV<gammaretro ordering reproduced; computed validates the documented tier; non-integrating=1.0 by mechanism; oracle abstains not fabricates; provenance curated; no-fabrication intact."
41
+ locked_files: [prereg/ws_genotox.yaml]
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pen-stack"
7
- version = "5.1.0"
7
+ version = "5.2.0"
8
8
  description = "Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,112 @@
1
+ """Build the COMPUTED genotoxicity-oracle artifact (v5.2, WS-GENOTOX).
2
+
3
+ For each integrating-vector CLASS we compute, from real data, how strongly its integration sites land near
4
+ known oncogenes — the data-grounded genotoxicity signal that replaces the hard-coded `genotoxicity` tier in
5
+ configs/delivery_vehicles.yaml for integrating vehicles:
6
+
7
+ risk(class) = P(an integration site of this class falls within `window_bp` of a COSMIC Cancer-Gene-
8
+ Census oncogene) [observed fraction over the class's site catalogue]
9
+ enrichment(class) = risk(class) / genome_background_frac [fold over a random-bin baseline]
10
+ genotox_score = min(1, 1 / enrichment) [1 = safest; episomal/non-targeting ~ 1.0]
11
+
12
+ Inputs (staged on the VM under /data; NOT shipped — this script runs where the data lives):
13
+ - /data/external/visdb/*.csv VISDB per-virus integration-site catalogues (hg38) [10.1093/nar/gkz867]
14
+ - /data/features/safety_annot.parquet per-1kb-bin dist_oncogene + genotoxic_cis (COSMIC CGC v104)
15
+ [COSMIC 10.1038/s41568-018-0060-1]
16
+ HIV ~= lentiviral integration biology (favors active gene bodies) [10.1016/S0092-8674(02)00864-4];
17
+ MLV ~= gammaretroviral (favors TSS/enhancers -> the LMO2 / SCID-X1 genotoxicity) [10.1126/science.1083413].
18
+
19
+ Output: a SMALL, committable YAML (configs/genotoxicity_oracle.yaml) of per-class summary statistics +
20
+ provenance — the raw catalogues stay on the VM; only the auditable summary ships. Magnitude (in-vivo clonal
21
+ outcome) is NOT modelled here and stays the `in_vivo_immunogenicity` / clonal known-unknown.
22
+
23
+ Run (on the VM, in the data-mounted image):
24
+ docker run --rm --entrypoint "" -v ~/data:/data -v ~/penstack:/app -w /app penstack:phase1.5 \\
25
+ python scripts/p52_build_genotox_oracle.py > configs/genotoxicity_oracle.yaml
26
+ """
27
+ from __future__ import annotations
28
+
29
+ import datetime as _dt
30
+ import glob
31
+ import os
32
+ import sys
33
+
34
+ import pandas as pd
35
+ import yaml
36
+
37
+ WINDOW_BP = 50_000
38
+ SAFETY_ANNOT = "/data/features/safety_annot.parquet"
39
+ VISDB_DIR = "/data/external/visdb"
40
+ ROBUST_MIN_N = 1000 # below this, a class is directional-only (flagged extrapolating)
41
+
42
+ # VISDB virus -> integration biology class + the palette vehicle(s) it grounds.
43
+ VIRUS_CLASS = {"HIV": "lentiviral", "HTLV": "deltaretroviral",
44
+ "MLV": "gammaretroviral", "XMLV": "gammaretroviral"}
45
+ # which palette vehicles each computed class grounds (others are non-integrating -> handled by mechanism)
46
+ CLASS_VEHICLES = {"lentiviral": ["lentivirus"]}
47
+
48
+ PROVENANCE_DOIS = ["10.1093/nar/gkz867", "10.1038/s41568-018-0060-1",
49
+ "10.1016/S0092-8674(02)00864-4", "10.1126/science.1083413"]
50
+
51
+
52
+ def _sites(csv: str, main_chroms: set[str]) -> pd.DataFrame:
53
+ df = pd.read_csv(csv, dtype=str)
54
+ cols = {c.lower().strip(): c for c in df.columns}
55
+ cc, sc = cols.get("human chromosome"), cols.get("hg38_start")
56
+ if not cc or not sc:
57
+ return pd.DataFrame(columns=["chrom", "bin"])
58
+ d = pd.DataFrame({
59
+ "chrom": df[cc].astype(str).map(lambda x: x if x.startswith("chr") else f"chr{x}"),
60
+ "pos": pd.to_numeric(df[sc], errors="coerce")}).dropna()
61
+ d = d[d["chrom"].isin(main_chroms)].copy()
62
+ d["bin"] = (d["pos"].astype(int) // 1000)
63
+ return d[["chrom", "bin"]]
64
+
65
+
66
+ def build() -> dict:
67
+ sa = pd.read_parquet(SAFETY_ANNOT)[["chrom", "bin", "dist_oncogene", "genotoxic_cis"]]
68
+ main = set(sa["chrom"].unique())
69
+ background = float((sa["dist_oncogene"] <= WINDOW_BP).mean())
70
+
71
+ classes: dict[str, dict] = {}
72
+ for csv in sorted(glob.glob(os.path.join(VISDB_DIR, "*.csv"))):
73
+ virus = os.path.basename(csv).replace(".csv", "")
74
+ cls = VIRUS_CLASS.get(virus)
75
+ if cls is None:
76
+ continue
77
+ m = _sites(csv, main).merge(sa, on=["chrom", "bin"], how="inner")
78
+ n = int(len(m))
79
+ if n == 0:
80
+ continue
81
+ frac = float((m["dist_oncogene"] <= WINDOW_BP).mean())
82
+ ci95 = float(1.96 * (frac * (1 - frac) / n) ** 0.5)
83
+ enrich = float(frac / background) if background else None
84
+ rec = {"virus": virus, "n_sites": n,
85
+ "frac_oncogene_50kb": round(frac, 5), "ci95": round(ci95, 5),
86
+ "enrichment": round(enrich, 3) if enrich else None,
87
+ "frac_genotoxic_cis": round(float(m["genotoxic_cis"].mean()), 6),
88
+ "median_dist_oncogene": int(m["dist_oncogene"].median()),
89
+ "robust": n >= ROBUST_MIN_N}
90
+ # keep the larger-n catalogue if a class has multiple viruses (e.g. gammaretro: MLV+XMLV)
91
+ if cls not in classes or n > classes[cls]["n_sites"]:
92
+ classes[cls] = rec
93
+
94
+ return {
95
+ "version": "1.0",
96
+ "built": _dt.date.today().isoformat(),
97
+ "description": ("computed integration-site genotoxicity oracle: per vector class, the observed "
98
+ "enrichment of integration sites within window_bp of a COSMIC oncogene vs genome "
99
+ "background. genotox_score = min(1, 1/enrichment). In-vivo clonal outcome is NOT "
100
+ "modelled (stays a known-unknown)."),
101
+ "window_bp": WINDOW_BP,
102
+ "genome_background_frac_oncogene_50kb": round(background, 5),
103
+ "inputs": {"visdb": "VISDB per-virus hg38 catalogues", "oncogenes": "COSMIC CGC v104 (safety_annot)"},
104
+ "provenance_dois": PROVENANCE_DOIS,
105
+ "robust_min_n": ROBUST_MIN_N,
106
+ "classes": classes,
107
+ "vehicle_class": CLASS_VEHICLES,
108
+ }
109
+
110
+
111
+ if __name__ == "__main__":
112
+ yaml.safe_dump(build(), sys.stdout, sort_keys=False, default_flow_style=False)
File without changes
File without changes
File without changes