pen-stack 7.1.8__tar.gz → 7.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 (579) hide show
  1. {pen_stack-7.1.8 → pen_stack-7.2.0}/CHANGELOG.md +66 -0
  2. {pen_stack-7.1.8 → pen_stack-7.2.0}/CITATION.cff +2 -2
  3. {pen_stack-7.1.8 → pen_stack-7.2.0}/MANIFEST.in +1 -0
  4. {pen_stack-7.1.8 → pen_stack-7.2.0}/PKG-INFO +1 -1
  5. pen_stack-7.2.0/data/curated/cast_systems.yaml +59 -0
  6. pen_stack-7.2.0/data/curated/integrase_att.yaml +43 -0
  7. pen_stack-7.2.0/data/offtarget/enumerated_cache.parquet +0 -0
  8. pen_stack-7.2.0/data/offtarget/motif_cache.parquet +0 -0
  9. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/cards/offtarget_data.md +26 -0
  10. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/__init__.py +1 -1
  11. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/mcp_server.py +9 -6
  12. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/api/manifest.py +7 -5
  13. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/server/api.py +23 -5
  14. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/offtarget_assay.py +24 -1
  15. pen_stack-7.2.0/pen_stack/wgenome/offtarget_bridge.py +47 -0
  16. pen_stack-7.2.0/pen_stack/wgenome/offtarget_cast.py +97 -0
  17. pen_stack-7.2.0/pen_stack/wgenome/offtarget_enumerate.py +271 -0
  18. pen_stack-7.2.0/pen_stack/wgenome/offtarget_integrase.py +88 -0
  19. pen_stack-7.2.0/pen_stack/wgenome/offtarget_nuclease.py +66 -0
  20. pen_stack-7.2.0/pen_stack/wgenome/offtarget_paste.py +41 -0
  21. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/offtarget_predict.py +35 -14
  22. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack.egg-info/PKG-INFO +1 -1
  23. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack.egg-info/SOURCES.txt +12 -0
  24. pen_stack-7.2.0/prereg/SHA256_LOCK_ws_offtarget2.json +8 -0
  25. pen_stack-7.2.0/prereg/ws_offtarget2.yaml +99 -0
  26. {pen_stack-7.1.8 → pen_stack-7.2.0}/pyproject.toml +1 -1
  27. {pen_stack-7.1.8 → pen_stack-7.2.0}/LICENSE +0 -0
  28. {pen_stack-7.1.8 → pen_stack-7.2.0}/README.md +0 -0
  29. {pen_stack-7.1.8 → pen_stack-7.2.0}/bench/run.py +0 -0
  30. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/chat_grounding/README.md +0 -0
  31. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/chat_grounding/SHA256SUMS +0 -0
  32. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/chat_headtohead/README.md +0 -0
  33. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/chat_routing/README.md +0 -0
  34. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/chat_routing/SHA256SUMS +0 -0
  35. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/chat_safety/README.md +0 -0
  36. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/chat_safety/SHA256SUMS +0 -0
  37. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/delivery/SHA256SUMS +0 -0
  38. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
  39. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/genome_writing_bench/README.md +0 -0
  40. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
  41. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  42. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
  43. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/genome_writing_challenge/README.md +0 -0
  44. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
  45. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/loop/SHA256SUMS +0 -0
  46. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/offtarget/SHA256SUMS +0 -0
  47. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/oracle/SHA256SUMS +0 -0
  48. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/position_effect/README.md +0 -0
  49. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/position_effect/SHA256SUMS +0 -0
  50. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/verify/SHA256SUMS +0 -0
  51. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/writer_efficiency/README.md +0 -0
  52. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/writer_efficiency/SHA256SUMS +0 -0
  53. {pen_stack-7.1.8 → pen_stack-7.2.0}/benchmarks/writespec/SHA256SUMS +0 -0
  54. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/aav_serotype_tropism.yaml +0 -0
  55. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/antipeg.yaml +0 -0
  56. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/atlas_families.yaml +0 -0
  57. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/bridge_offtarget_profile.yaml +0 -0
  58. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/calibration/preexisting_nab_independent.yaml +0 -0
  59. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/capsid_epitope_oracle.yaml +0 -0
  60. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/capsid_sequences.fasta +0 -0
  61. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/cargo_polish.yaml +0 -0
  62. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/cell_types.yaml +0 -0
  63. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/datasets.yaml +0 -0
  64. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/delivery_constraints.yaml +0 -0
  65. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/delivery_rules.yaml +0 -0
  66. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/delivery_vehicles.yaml +0 -0
  67. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/expression/modifiers.yaml +0 -0
  68. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/expression/promoters.yaml +0 -0
  69. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/gates_v3.yaml +0 -0
  70. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/genotoxicity_oracle.yaml +0 -0
  71. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/gsh_validated_heldout.yaml +0 -0
  72. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/intent_weights.yaml +0 -0
  73. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/known_unknowns.yaml +0 -0
  74. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/llm.yaml +0 -0
  75. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/metric_guide.yaml +0 -0
  76. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/mhc_epitope_oracle.yaml +0 -0
  77. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/monitor_queries.yaml +0 -0
  78. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/oracles/execution.yaml +0 -0
  79. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/oracles/reliability.yaml +0 -0
  80. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/oracles/scope_cards.yaml +0 -0
  81. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/rules/compliance.yaml +0 -0
  82. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/rules/delivery.yaml +0 -0
  83. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/rules/fold.yaml +0 -0
  84. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/rules/multiplex.yaml +0 -0
  85. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/rules/payload.yaml +0 -0
  86. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/rules/reachability.yaml +0 -0
  87. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/safety/hazard_registry.yaml +0 -0
  88. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/safety/policy.yaml +0 -0
  89. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/safety/probes.yaml +0 -0
  90. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/score_axes.yaml +0 -0
  91. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/seroprevalence.yaml +0 -0
  92. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/target_sites.yaml +0 -0
  93. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/universe_crosswalk.yaml +0 -0
  94. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/write_types.yaml +0 -0
  95. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/writer_sequences.fasta +0 -0
  96. {pen_stack-7.1.8 → pen_stack-7.2.0}/configs/wtkb_curated.yaml +0 -0
  97. {pen_stack-7.1.8 → pen_stack-7.2.0}/data/curated/bridge_offtarget_energetics.json +0 -0
  98. {pen_stack-7.1.8 → pen_stack-7.2.0}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  99. {pen_stack-7.1.8 → pen_stack-7.2.0}/data/curated/gene_coords.parquet +0 -0
  100. {pen_stack-7.1.8 → pen_stack-7.2.0}/data/curated/unified_editor_universe.parquet +0 -0
  101. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/BACKLOG.md +0 -0
  102. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/DEPLOY.md +0 -0
  103. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/INFRA.md +0 -0
  104. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/MCP.md +0 -0
  105. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/RELEASING.md +0 -0
  106. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/REPRO.md +0 -0
  107. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/STABILITY.md +0 -0
  108. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/agent.md +0 -0
  109. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/alphagenome_feasibility.md +0 -0
  110. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/autonomy.md +0 -0
  111. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/benchmark_circularity.md +0 -0
  112. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/biosecurity.md +0 -0
  113. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/build_interface.md +0 -0
  114. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/cards/atlas.md +0 -0
  115. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/cards/durability.md +0 -0
  116. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/cards/position_effect_data.md +0 -0
  117. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/cards/safety.md +0 -0
  118. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/cards/writer_efficiency_data.md +0 -0
  119. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/challenge.md +0 -0
  120. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/closed_loop.md +0 -0
  121. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/co_scientist.md +0 -0
  122. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/co_scientist_loop.md +0 -0
  123. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/delivery.md +0 -0
  124. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/delivery_immunology.md +0 -0
  125. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/delivery_recommender.md +0 -0
  126. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/digital_twin.md +0 -0
  127. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/dissemination.md +0 -0
  128. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/environment.md +0 -0
  129. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/experiment_design.md +0 -0
  130. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/generative_design.md +0 -0
  131. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/immune_profiler.md +0 -0
  132. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/index.md +0 -0
  133. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/integrations.md +0 -0
  134. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/live_oracles.md +0 -0
  135. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/mechanistic_constraints.md +0 -0
  136. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/offtarget.md +0 -0
  137. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/oracle_mesh.md +0 -0
  138. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/oracles.md +0 -0
  139. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/position_effect.md +0 -0
  140. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/positioning.md +0 -0
  141. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/private_data_formats.md +0 -0
  142. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/quickstart.md +0 -0
  143. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/responsible_use.md +0 -0
  144. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/rule_spec.md +0 -0
  145. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/rules.md +0 -0
  146. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/scope.md +0 -0
  147. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/scorecard.md +0 -0
  148. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/tpe_bench.md +0 -0
  149. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/tutorials/compare-families.md +0 -0
  150. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/tutorials/score-deliverability.md +0 -0
  151. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/tutorials/where-can-i-write.md +0 -0
  152. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  153. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/uncertainty.md +0 -0
  154. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/verify.md +0 -0
  155. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/verify_service.md +0 -0
  156. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/world_model.md +0 -0
  157. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/writer_efficiency.md +0 -0
  158. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/writer_verification.md +0 -0
  159. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/writespec_bench.md +0 -0
  160. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/writespec_profile.md +0 -0
  161. {pen_stack-7.1.8 → pen_stack-7.2.0}/docs/wtkb.md +0 -0
  162. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/_resources.py +0 -0
  163. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/active/__init__.py +0 -0
  164. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/active/acquire.py +0 -0
  165. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/active/brains.py +0 -0
  166. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/active/campaign.py +0 -0
  167. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/active/design.py +0 -0
  168. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/active/validate.py +0 -0
  169. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/adapt/__init__.py +0 -0
  170. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/adapt/finetune.py +0 -0
  171. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/adapt/ingest.py +0 -0
  172. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/adapt/pipeline.py +0 -0
  173. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/adapt/recalibrate.py +0 -0
  174. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/adapt/report.py +0 -0
  175. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/__init__.py +0 -0
  176. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/cite.py +0 -0
  177. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/co_scientist.py +0 -0
  178. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/epistemic.py +0 -0
  179. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/guardrails.py +0 -0
  180. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/orchestrator.py +0 -0
  181. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/orchestrator_live.py +0 -0
  182. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/pen_agent.py +0 -0
  183. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/scope.py +0 -0
  184. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/agent/tools.py +0 -0
  185. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/api/__init__.py +0 -0
  186. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/__init__.py +0 -0
  187. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/build_wtkb.py +0 -0
  188. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/crosslink.py +0 -0
  189. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/expand.py +0 -0
  190. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/guide_design.py +0 -0
  191. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/schema.py +0 -0
  192. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/scorecard.py +0 -0
  193. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/universe.py +0 -0
  194. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/variant_propose.py +0 -0
  195. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/writer_efficiency.py +0 -0
  196. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/writer_predict.py +0 -0
  197. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/writer_recommend.py +0 -0
  198. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/atlas/writer_verify.py +0 -0
  199. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/__init__.py +0 -0
  200. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/activity.py +0 -0
  201. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/cli.py +0 -0
  202. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/fold_qc.py +0 -0
  203. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/guide_qc.py +0 -0
  204. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/ingest.py +0 -0
  205. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/offtarget.py +0 -0
  206. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/offtarget_energetics.py +0 -0
  207. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/ortholog_screen.py +0 -0
  208. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/bridge/pipeline.py +0 -0
  209. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/build/__init__.py +0 -0
  210. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/build/cloudlab.py +0 -0
  211. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/build/ingest.py +0 -0
  212. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/build/protocol.py +0 -0
  213. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/build/simlab.py +0 -0
  214. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/cli.py +0 -0
  215. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/data/__init__.py +0 -0
  216. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/data/encode.py +0 -0
  217. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/data/genome.py +0 -0
  218. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/data/ingest_chromatin.py +0 -0
  219. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/data/ingest_integration.py +0 -0
  220. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/data/ingest_safety_annot.py +0 -0
  221. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/data/ingest_trip.py +0 -0
  222. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/design/__init__.py +0 -0
  223. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/design/capsid_generate.py +0 -0
  224. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/design/generate.py +0 -0
  225. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/design/pareto.py +0 -0
  226. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/design/space.py +0 -0
  227. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/design/writer_variants.py +0 -0
  228. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/env/__init__.py +0 -0
  229. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/env/genome_writing_env.py +0 -0
  230. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/env/policies.py +0 -0
  231. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/graph/__init__.py +0 -0
  232. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/graph/build.py +0 -0
  233. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/graph/cell_types.py +0 -0
  234. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/graph/ingest.py +0 -0
  235. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/graph/query.py +0 -0
  236. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/graph/schema.py +0 -0
  237. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/loop/__init__.py +0 -0
  238. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/loop/continual.py +0 -0
  239. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/loop/cycle.py +0 -0
  240. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/loop/drift.py +0 -0
  241. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/mech/__init__.py +0 -0
  242. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/mech/classify_atlas.py +0 -0
  243. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/mech/whitelist.py +0 -0
  244. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/monitor/__init__.py +0 -0
  245. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/monitor/europepmc.py +0 -0
  246. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/monitor/run.py +0 -0
  247. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/monitor/triage.py +0 -0
  248. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/__init__.py +0 -0
  249. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/affinity.py +0 -0
  250. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/cache.py +0 -0
  251. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/energetics.py +0 -0
  252. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/genome.py +0 -0
  253. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/protein_design.py +0 -0
  254. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/reliability.py +0 -0
  255. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/rna.py +0 -0
  256. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/schema.py +0 -0
  257. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/status.py +0 -0
  258. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/structure.py +0 -0
  259. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/structure_run.py +0 -0
  260. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/oracles/vcell.py +0 -0
  261. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/__init__.py +0 -0
  262. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/ada_risk.py +0 -0
  263. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/antipeg_oracle.py +0 -0
  264. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
  265. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/cargo.py +0 -0
  266. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/cargo_polish.py +0 -0
  267. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/chromosome.py +0 -0
  268. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/delivery.py +0 -0
  269. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/delivery_constraints.py +0 -0
  270. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/delivery_immune.py +0 -0
  271. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/delivery_immunology.py +0 -0
  272. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/delivery_predict.py +0 -0
  273. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/delivery_vehicles.py +0 -0
  274. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/genotoxicity_oracle.py +0 -0
  275. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/immune_mhc2.py +0 -0
  276. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/immune_profile.py +0 -0
  277. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/innate_sensing.py +0 -0
  278. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/multiplex.py +0 -0
  279. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/optimize.py +0 -0
  280. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/pipeline.py +0 -0
  281. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/report.py +0 -0
  282. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/router.py +0 -0
  283. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/seroprevalence_oracle.py +0 -0
  284. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/planner/target_site.py +0 -0
  285. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/__init__.py +0 -0
  286. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/corpus.py +0 -0
  287. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/embed.py +0 -0
  288. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/ground.py +0 -0
  289. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/index.py +0 -0
  290. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/llm.py +0 -0
  291. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/qa.py +0 -0
  292. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rag/retrieve.py +0 -0
  293. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rules/__init__.py +0 -0
  294. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rules/evaluators.py +0 -0
  295. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rules/loader.py +0 -0
  296. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rules/schema.py +0 -0
  297. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rules/solver.py +0 -0
  298. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/rules/spec.py +0 -0
  299. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/__init__.py +0 -0
  300. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/audit.py +0 -0
  301. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/gate.py +0 -0
  302. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/policy.py +0 -0
  303. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/redteam.py +0 -0
  304. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/registry.py +0 -0
  305. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/screen.py +0 -0
  306. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/safety/standards.py +0 -0
  307. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/score/__init__.py +0 -0
  308. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/score/recalibrate.py +0 -0
  309. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/score/therapeutic.py +0 -0
  310. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/server/__init__.py +0 -0
  311. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/__init__.py +0 -0
  312. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/clarify.py +0 -0
  313. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/extract.py +0 -0
  314. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/resolvers/__init__.py +0 -0
  315. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/resolvers/cell.py +0 -0
  316. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/resolvers/chem.py +0 -0
  317. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/resolvers/feature.py +0 -0
  318. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/resolvers/gene.py +0 -0
  319. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/resolvers/locus.py +0 -0
  320. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/resolvers/phenotype.py +0 -0
  321. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/satisfy.py +0 -0
  322. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/service.py +0 -0
  323. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/spec/writespec.py +0 -0
  324. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/twin/__init__.py +0 -0
  325. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/twin/calibrate.py +0 -0
  326. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/twin/data/__init__.py +0 -0
  327. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/twin/data/position_effect.py +0 -0
  328. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/twin/mechanistic.py +0 -0
  329. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/twin/outcome.py +0 -0
  330. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/twin/position_effect.py +0 -0
  331. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/ui/__init__.py +0 -0
  332. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/ui/app.py +0 -0
  333. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/__init__.py +0 -0
  334. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/adapt_demo.py +0 -0
  335. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/agent_eval.py +0 -0
  336. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  337. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
  338. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/bench_graph_tasks.py +0 -0
  339. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/bench_rule_tasks.py +0 -0
  340. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/bench_trust_tasks.py +0 -0
  341. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  342. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  343. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/cargo_directionality.py +0 -0
  344. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/closed_loop.py +0 -0
  345. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/durability_baselines.py +0 -0
  346. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/experiment_design.py +0 -0
  347. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/expr_controls.py +0 -0
  348. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/forward_hypotheses.py +0 -0
  349. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/generative_design.py +0 -0
  350. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/guide_qc_demo.py +0 -0
  351. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/heldout_celltype_expr.py +0 -0
  352. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/immune_calibration.py +0 -0
  353. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/intent_specification.py +0 -0
  354. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/known_biology_expr.py +0 -0
  355. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  356. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  357. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/outcome_calibration.py +0 -0
  358. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/outcome_prediction.py +0 -0
  359. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/paper3_benchmark.py +0 -0
  360. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/paper4_real_validation.py +0 -0
  361. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/paper4_validation.py +0 -0
  362. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/protocol_safety.py +0 -0
  363. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/safety_screening.py +0 -0
  364. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/selective_prediction.py +0 -0
  365. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/seq_vs_measured.py +0 -0
  366. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/target_site_controls.py +0 -0
  367. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/uncertainty_eval.py +0 -0
  368. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/ungrounded_baseline.py +0 -0
  369. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/within_locus_ranking.py +0 -0
  370. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/validate/writer_recovery.py +0 -0
  371. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/verify/__init__.py +0 -0
  372. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/verify/proof.py +0 -0
  373. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/verify/schema.py +0 -0
  374. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/verify/service.py +0 -0
  375. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/web/__init__.py +0 -0
  376. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/web/guide.py +0 -0
  377. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/web/llm.py +0 -0
  378. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/web/llm_provider.py +0 -0
  379. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/web/router.py +0 -0
  380. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/web/server.py +0 -0
  381. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/web/tools.py +0 -0
  382. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/__init__.py +0 -0
  383. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/chromatin_seq.py +0 -0
  384. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/durability.py +0 -0
  385. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/export_tracks.py +0 -0
  386. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/features.py +0 -0
  387. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/gsh_baseline.py +0 -0
  388. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/mesh_features.py +0 -0
  389. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/offtarget_data.py +0 -0
  390. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/ood.py +0 -0
  391. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/providers.py +0 -0
  392. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/safety.py +0 -0
  393. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/structure3d.py +0 -0
  394. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/uncertainty.py +0 -0
  395. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack/wgenome/writability.py +0 -0
  396. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack.egg-info/dependency_links.txt +0 -0
  397. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack.egg-info/entry_points.txt +0 -0
  398. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack.egg-info/requires.txt +0 -0
  399. {pen_stack-7.1.8 → pen_stack-7.2.0}/pen_stack.egg-info/top_level.txt +0 -0
  400. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_phase0.json +0 -0
  401. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  402. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_phase2.json +0 -0
  403. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_phase3.json +0 -0
  404. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_a.json +0 -0
  405. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_acq.json +0 -0
  406. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
  407. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
  408. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  409. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_b.json +0 -0
  410. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  411. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  412. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  413. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  414. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_c.json +0 -0
  415. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  416. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_calib.json +0 -0
  417. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
  418. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_chat.json +0 -0
  419. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_cite.json +0 -0
  420. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_closedloop.json +0 -0
  421. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_continual.json +0 -0
  422. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
  423. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_crit.json +0 -0
  424. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  425. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_d.json +0 -0
  426. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_delivery.json +0 -0
  427. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_drift.json +0 -0
  428. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_e.json +0 -0
  429. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_env.json +0 -0
  430. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  431. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
  432. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_expr2.json +0 -0
  433. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_f.json +0 -0
  434. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
  435. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_g.json +0 -0
  436. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_gen.json +0 -0
  437. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
  438. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  439. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_h.json +0 -0
  440. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
  441. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_immune.json +0 -0
  442. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_immune2.json +0 -0
  443. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
  444. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_innate.json +0 -0
  445. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_loop.json +0 -0
  446. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
  447. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  448. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
  449. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_mech.json +0 -0
  450. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  451. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_o.json +0 -0
  452. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_offtarget.json +0 -0
  453. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
  454. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_oracle.json +0 -0
  455. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_orch.json +0 -0
  456. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
  457. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
  458. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_peg.json +0 -0
  459. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_penchat.json +0 -0
  460. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_plan.json +0 -0
  461. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_policy.json +0 -0
  462. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_profile.json +0 -0
  463. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_proto.json +0 -0
  464. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_r.json +0 -0
  465. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
  466. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_route.json +0 -0
  467. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_screen.json +0 -0
  468. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
  469. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
  470. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
  471. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  472. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_v.json +0 -0
  473. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
  474. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_verify.json +0 -0
  475. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_writer.json +0 -0
  476. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_writespec.json +0 -0
  477. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  478. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/paper1.yaml +0 -0
  479. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/paper2.yaml +0 -0
  480. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/paper3.yaml +0 -0
  481. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/paper4.yaml +0 -0
  482. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/phase0.yaml +0 -0
  483. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_a.yaml +0 -0
  484. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_acq.yaml +0 -0
  485. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_aldesign.yaml +0 -0
  486. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_alvalidate.yaml +0 -0
  487. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_atlas.yaml +0 -0
  488. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_b.yaml +0 -0
  489. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_ba.yaml +0 -0
  490. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_ba_v33.yaml +0 -0
  491. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_ba_v45.yaml +0 -0
  492. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_bench.yaml +0 -0
  493. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_c.yaml +0 -0
  494. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_cal.yaml +0 -0
  495. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_calib.yaml +0 -0
  496. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_challenge.yaml +0 -0
  497. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_chat.yaml +0 -0
  498. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_cite.yaml +0 -0
  499. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_closedloop.yaml +0 -0
  500. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_continual.yaml +0 -0
  501. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_cosci2.yaml +0 -0
  502. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_crit.yaml +0 -0
  503. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_ct.yaml +0 -0
  504. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_d.yaml +0 -0
  505. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_delivery.yaml +0 -0
  506. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_drift.yaml +0 -0
  507. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_e.yaml +0 -0
  508. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_env.yaml +0 -0
  509. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_ep.yaml +0 -0
  510. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_epitope.yaml +0 -0
  511. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_expr2.yaml +0 -0
  512. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_f.yaml +0 -0
  513. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_frontend.yaml +0 -0
  514. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_g.yaml +0 -0
  515. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_gen.yaml +0 -0
  516. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_genotox.yaml +0 -0
  517. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_graph.yaml +0 -0
  518. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_h.yaml +0 -0
  519. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_hybrid.yaml +0 -0
  520. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_immune.yaml +0 -0
  521. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_immune2.yaml +0 -0
  522. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_ingest.yaml +0 -0
  523. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_innate.yaml +0 -0
  524. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_loop.yaml +0 -0
  525. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_manifest.yaml +0 -0
  526. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_mc.yaml +0 -0
  527. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_mcp.yaml +0 -0
  528. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_mech.yaml +0 -0
  529. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_mon.yaml +0 -0
  530. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_o.yaml +0 -0
  531. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_offtarget.yaml +0 -0
  532. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_openapi.yaml +0 -0
  533. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_oracle.yaml +0 -0
  534. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_orch.yaml +0 -0
  535. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_outcome.yaml +0 -0
  536. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_pareto.yaml +0 -0
  537. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_peg.yaml +0 -0
  538. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_penchat.yaml +0 -0
  539. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_plan.yaml +0 -0
  540. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_policy.yaml +0 -0
  541. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_profile.yaml +0 -0
  542. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_proto.yaml +0 -0
  543. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_r.yaml +0 -0
  544. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_redteam.yaml +0 -0
  545. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_route.yaml +0 -0
  546. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_screen.yaml +0 -0
  547. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_seroprev.yaml +0 -0
  548. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_simlab.yaml +0 -0
  549. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_twincal.yaml +0 -0
  550. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_uq.yaml +0 -0
  551. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_v.yaml +0 -0
  552. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_vcell.yaml +0 -0
  553. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_verify.yaml +0 -0
  554. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_writer.yaml +0 -0
  555. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_writespec.yaml +0 -0
  556. {pen_stack-7.1.8 → pen_stack-7.2.0}/prereg/ws_wv.yaml +0 -0
  557. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/build_capsid_fitness.py +0 -0
  558. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/build_rag_corpus.py +0 -0
  559. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/calibrate_immune_axes.py +0 -0
  560. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/fetch_licensed_sources.py +0 -0
  561. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/offtarget_chromatin_incremental.py +0 -0
  562. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/offtarget_chromatin_matched.py +0 -0
  563. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/offtarget_chromatin_validation.py +0 -0
  564. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_build_atlas.py +0 -0
  565. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_build_durability.py +0 -0
  566. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_build_position_effect.py +0 -0
  567. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_build_writer_eff.py +0 -0
  568. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_export_tracks.py +0 -0
  569. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_safety_concordance.py +0 -0
  570. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_train_safety.py +0 -0
  571. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p1_validation_report.py +0 -0
  572. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p2_build_atlas.py +0 -0
  573. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p3_benchmark_report.py +0 -0
  574. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p4_genome_scan.py +0 -0
  575. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p52_build_genotox_oracle.py +0 -0
  576. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/p53_build_epitope_oracle.py +0 -0
  577. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/ws_b_report.py +0 -0
  578. {pen_stack-7.1.8 → pen_stack-7.2.0}/scripts/ws_c_report.py +0 -0
  579. {pen_stack-7.1.8 → pen_stack-7.2.0}/setup.cfg +0 -0
@@ -3,6 +3,72 @@
3
3
  All notable changes to PEN-STACK are documented here. This file follows
4
4
  [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
+ ## [7.2.0] - 2026-07-01 - PEN-OFFTGT v2: Stage E becomes a genome-wide, per-mechanism off-target FINDER (all 5 writer classes)
7
+
8
+ Stage E was a candidate *scorer* — it ranked off-target sites you supplied. A real off-target tool (CRISPOR /
9
+ CHOPCHOP) takes a guide/target and *finds* the genome-wide off-target set itself. PEN-OFFTGT v2 closes that
10
+ enumeration gap AND applies the **correct off-target mechanism for each writer class**, each carrying a truthful
11
+ per-mechanism validation status (O-WS0–O-WS9 of the plan).
12
+
13
+ ### Per-mechanism paths (O-WS3–O-WS6)
14
+ - **Serine integrase** (`offtarget_integrase.py`, O-WS3): a genome-wide **pseudo-attP** scan — a fixed-sequence
15
+ Cas-OFFinder scan of the attP core window over GRCh38, scored by att-arm similarity. **Bxb1** is fully encoded
16
+ (FlyBase FBto0000359 / Ghosh 2003) and is highly specific. Status **semi-validated**. **PhiC31 is a disclosed
17
+ data gap** (its documented human pseudo-attP set — Chalberg 2006 — could not be verified from an open source in
18
+ this build, so it abstains rather than fabricate). `data/curated/integrase_att.yaml`.
19
+ - **Bridge** (`offtarget_bridge.py`, O-WS4): wraps the existing DMS-scored genome scan (measured Perry-2025
20
+ specificity, held-out ranking AUROC 0.88). Status **hard-locked mechanism-based-unvalidated** — the ranker is
21
+ DMS-validated but **no genome-wide cellular off-target assay exists** for bridge recombinases, so genomic
22
+ recovery is unvalidated (stated in a no-ground-truth disclosure).
23
+ - **CAST** (`offtarget_cast.py` + `data/curated/cast_systems.yaml`, O-WS5) — NEW: guide-directed spacer scan +
24
+ the distinctive **guide-independent untargeted-transposition** background per system (ShCAST/Type V-K high +
25
+ AT-biased; VchCAST/Type I-F >95–99% on-target), all DOI-tagged. Status **mechanism-based-unvalidated**.
26
+ - **PASTE / (ee)PASSIGE** (`offtarget_paste.py`, O-WS6) — NEW: composes the validated nuclease finder (Cas9
27
+ nickase, pegRNA) with the semi-validated integrase pseudo-attP scan (installed att); returns both component sets
28
+ and recommends BOTH a nuclease assay and an integrase assay. Status **composite**.
29
+
30
+ ### Status labels + assay recommender (O-WS7) + disclosures (O-WS8 / gate O-G3)
31
+ - Every nomination carries a per-mechanism status label + the mechanism-appropriate confirm assay; `recommend_assay`
32
+ now covers CAST (transposon insertion-site seq) and PASTE (dual). `docs/cards/offtarget_data.md` documents the
33
+ per-mechanism ground-truth status — **O-G3: every mechanism has either a sealed benchmark (nuclease) or an
34
+ explicit no-ground-truth / data-gap disclosure (integrase-PhiC31, bridge, CAST) — never a fabricated metric.**
35
+ - Disclosed deviations from the plan (no silent substitution): `att_pwm.json` → an att-similarity model in
36
+ `integrase_att.yaml` (a per-position PWM needs aligned pseudosites that do not exist at genome scale);
37
+ `bridge_dms_specificity.parquet` → the existing measured `bridge_offtarget_profile_measured.parquet` is reused;
38
+ `known_pseudosites.parquet` → not created (Bxb1 highly specific; PhiC31 disclosed data gap).
39
+
40
+ ### Nuclease finder (O-WS0–O-WS2)
41
+
42
+ ### Added
43
+ - **Genome-wide enumeration engine** (`pen_stack/wgenome/offtarget_enumerate.py`, O-WS1). Given a guide + enzyme,
44
+ enumerates every genomic site within the mismatch tolerance across GRCh38 via **Cas-OFFinder** (Bae, Park & Kim,
45
+ Bioinformatics 2014, `10.1093/bioinformatics/btu048`), returning coordinates, strand, matched sequence, and
46
+ mismatch count. Supports SpCas9 (NGG), SaCas9 (NNGRRT), and Cas12a (TTTV, 5' PAM). A full scan is heavy, so it
47
+ runs **only on the VM** (`casoffinder:tools` Docker image, `docker/casoffinder.Dockerfile`); the live app
48
+ **replays a committed coordinate cache** for the canonical guides or **abstains honestly** for a novel one
49
+ (never fabricates sites) — the same replay-or-abstain pattern as the heavy structure oracles. The enumerated
50
+ coordinates are facts from the public GRCh38 assembly (no license restriction), so the cache is committed
51
+ (`data/offtarget/enumerated_cache.parquet`, 8 canonical guides, 40,268 sites).
52
+ - **Nuclease off-target FINDER** (`pen_stack/wgenome/offtarget_nuclease.py`, O-WS2): chains enumeration into the
53
+ existing, validated scorer — real CRISOT-Score → mismatch-calibrated risk band → chromatin annotation — so a
54
+ guide returns the genome-wide ranked off-target set *with coordinates*. Scoring is unchanged from v6.10 (still
55
+ validated: CRISOT beats homology on four unbiased assays); v2 adds the enumeration front end.
56
+ - **O-G1 gate PASSED**: enumeration recovers **100%** of EMX1's documented GUIDE-seq off-targets within the ≤5-mm
57
+ tolerance (all 16 validated-active sites, which are all ≤4 mm; the higher-mismatch fixture rows are inactive
58
+ negatives). The on-target is recovered at chr2:72,933,852. `tests/unit/test_ws_offtarget2.py`.
59
+ - **Prereg** `prereg/ws_offtarget2.yaml` (SHA-locked): the mechanism taxonomy, per-enzyme enumeration parameters,
60
+ the truthful per-mechanism status-label rules (validated / semi-validated / mechanism-based-unvalidated), and
61
+ the O-G1/G2/G3 gates.
62
+ - REST: `POST /offtarget` is now a **finder by default** for a nuclease guide with no supplied `candidate_sites`
63
+ (accepts `enzyme`, `max_mismatch`); `GET /offtarget/enumerated` lists the cached guides. The web **Off-Target**
64
+ page is rebuilt as a finder (guide in → genome-wide ranked off-targets with coordinates), with the scorer→finder
65
+ explanation and the VM/cache architecture stated.
66
+
67
+ ### Unchanged / backward compatible
68
+ - Supplying `candidate_sites` keeps the v6.10 score-my-candidates path. The integrase (pseudo-attB sequence scan),
69
+ bridge (Perry-DMS), chromatin annotation, and validation-assay recommender are unchanged. Nomination remains a
70
+ candidate, never a clearance.
71
+
6
72
  ## [7.1.8] - 2026-06-30 - Writer immunogenicity moves to the Writer Atlas; SpCas9 removed from the writer options
7
73
 
8
74
  ### Changed
@@ -1,8 +1,8 @@
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: 7.1.8
5
- date-released: 2026-06-30
4
+ version: 7.2.0
5
+ date-released: 2026-07-01
6
6
  authors:
7
7
  - family-names: "Mahaboob Ali"
8
8
  given-names: "Anees Ahmed"
@@ -5,6 +5,7 @@ include README.md LICENSE CHANGELOG.md CITATION.cff pyproject.toml
5
5
  recursive-include configs *.yaml *.yml *.txt.example *.fasta
6
6
  recursive-include prereg *.yaml *.json
7
7
  recursive-include data/curated *
8
+ recursive-include data/offtarget *.parquet
8
9
  recursive-include benchmarks *.yaml *.md SHA256SUMS
9
10
  recursive-include bench *.py
10
11
  recursive-include docs *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 7.1.8
3
+ Version: 7.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
@@ -0,0 +1,59 @@
1
+ # CRISPR-associated transposase (CAST) systems — per-system off-target properties (PEN-OFFTGT v2, O-WS5).
2
+ #
3
+ # CAST off-target has TWO distinct modes, and the balance differs by system type:
4
+ # 1. guide-directed — integration at genomic sites matching the crRNA spacer (a sequence-match scan, like a
5
+ # nuclease off-target, but the transposon inserts ~49-66 bp downstream of the protospacer).
6
+ # 2. guide-INDEPENDENT untargeted transposition — the Tn7-like transposase machinery (TnsB/TnsC/TniQ) inserting
7
+ # WITHOUT the CRISPR effector or the guide. This is the DISTINCTIVE, dominant off-target mode
8
+ # for Type V-K systems (ShCAST) and is largely absent in Type I-F systems (VchCAST).
9
+ #
10
+ # All values are DOCUMENTED facts from the primary literature (DOIs below), reported as a qualitative tier plus the
11
+ # published on-target fidelity where a number is stated. This is NOT a genome-wide cellular off-target assay
12
+ # (none exists for CAST at genome scale) -> status is mechanism_based_unvalidated; the confirming assay is
13
+ # transposon insertion-site sequencing.
14
+
15
+ version: "1.0"
16
+
17
+ systems:
18
+ ShCAST:
19
+ full_name: "Scytonema hofmanni CAST"
20
+ cast_type: "V-K"
21
+ effector: "Cas12k"
22
+ untargeted_transposition: high # guide/Cas12k-INDEPENDENT, AT-biased (the dominant off-target mode)
23
+ guide_independent: true
24
+ at_biased_untargeted: true
25
+ on_target_fidelity_note: "up to ~80% integration in E. coli without selection, but LOW fidelity: prone to
26
+ extensive RNA-independent (guide-independent) transposition via TnsB/TnsC/TniQ; improved vectors that
27
+ suppress the RNA-independent pathway raise specificity up to 98.1% (Type V-K)."
28
+ dois:
29
+ - "10.1126/science.aax9181" # Strecker et al. 2019, ShCAST (Science) — RNA-guided DNA insertion with CAST
30
+ - "10.1126/science.adj8543" # Type V-K target-site selection + untargeted mechanism (Science 2024)
31
+ VchCAST:
32
+ full_name: "Vibrio cholerae CAST (Tn6677 / INTEGRATE)"
33
+ cast_type: "I-F"
34
+ effector: "Cascade (Cas6-Cas7-Cas8/Cas5) + TniQ"
35
+ untargeted_transposition: low # high fidelity; guide-independent transposition largely absent
36
+ guide_independent: false
37
+ at_biased_untargeted: false
38
+ on_target_fidelity_note: "remarkable fidelity — the majority of crRNAs direct >95% on-target integration and
39
+ many exceed 99%; up to ~100% integration efficiency of cargo up to ~10 kb in bacteria."
40
+ dois:
41
+ - "10.1038/s41586-019-1323-z" # Klompe et al. 2019, VchCAST / Tn6677 (Nature)
42
+ - "10.1038/s41587-020-00745-y" # Vo et al. 2021, INTEGRATE high-efficiency multiplexed (Nat Biotechnol)
43
+ evoCAST:
44
+ full_name: "evolved Scytonema hofmanni CAST (PACE-evolved)"
45
+ cast_type: "V-K"
46
+ effector: "Cas12k (evolved)"
47
+ untargeted_transposition: moderate # directed evolution improved specificity over WT ShCAST
48
+ guide_independent: true
49
+ at_biased_untargeted: true
50
+ on_target_fidelity_note: "evolved Type V-K CAST with substantially improved on-target integration in human
51
+ cells; guide-independent transposition is reduced relative to WT ShCAST but not eliminated."
52
+ dois:
53
+ - "10.1126/science.adr8492" # Tou et al. 2025, evoCAST (Science) — evolved CAST for human-cell insertion
54
+ - "10.1038/s41587-023-01748-1" # Lampe et al. 2024, targeted integration in human cells without DSBs (Nat Biotechnol)
55
+
56
+ # scope: human-cell CAST is emerging (evoCAST 2025); the untargeted-transposition rates above are characterised in
57
+ # bacteria / biochemically. No genome-wide unbiased CELLULAR off-target assay exists for human-cell CAST -> the
58
+ # genomic predictions are mechanism-based, unvalidated; confirm with transposon insertion-site sequencing.
59
+ confirm_assay: "transposon insertion-site sequencing (e.g. Tn-seq / integration-site amplicon sequencing)"
@@ -0,0 +1,43 @@
1
+ # Serine-integrase attachment sites for the genome-wide pseudo-att off-target scan (PEN-OFFTGT v2, O-WS3).
2
+ #
3
+ # A serine integrase recombines a donor attB with a genomic pseudo-attP resembling the phage attP. The off-target
4
+ # scan enumerates genomic sites similar to the attP core region (a fixed-sequence Cas-OFFinder scan over GRCh38,
5
+ # tolerating mismatches), then scores each by att-arm similarity. Status: SEMI-VALIDATED (documented pseudosites as
6
+ # partial ground truth). Confirming assay: Cryptic-seq / HIDE-seq.
7
+ #
8
+ # DEVIATION (disclosed): the prereg named `att_pwm.json`. A position-weight MATRIX requires many aligned pseudosite
9
+ # sequences to estimate per-position tolerance; that alignment does not exist at genome scale for these integrases.
10
+ # So the scan uses att-arm SIMILARITY (mismatch count to the documented att, core-anchored) rather than a fitted
11
+ # PWM — a mechanism-based similarity model, honestly labelled, not a fabricated per-position matrix.
12
+
13
+ version: "1.0"
14
+
15
+ integrases:
16
+ Bxb1:
17
+ origin: "phage Bxb1 (Mycobacterium)"
18
+ attB: "TCGGCCGGCTTGTCGACGACGGCGGTCTCCGTCGTCAGGATCATCCGGGC"
19
+ attP: "GTCGTGGTTTGTCTGGTCAACCACCGCGGTCTCAGTGGTGTACGGTACAAACCCCGAC"
20
+ core: "GCGGTCTC"
21
+ central_dinucleotide: "GT"
22
+ # 34-nt attP window centred on the core — the fixed sequence scanned genome-wide for pseudo-attP
23
+ scan_window: "CTGGTCAACCACCGCGGTCTCAGTGGTGTACGGT"
24
+ scan_max_mismatch: 8
25
+ specificity_note: "Bxb1 is highly specific — very few genomic pseudo-attP in human cells (a reason it is the
26
+ integrase of choice for PASTE/PASSIGE). The scan therefore returns few high-similarity candidates."
27
+ source: "FlyBase FBto0000359/358; Ghosh, Kim & Hatfull, Mol Cell 2003"
28
+ doi: "10.1016/S1097-2765(03)00444-1"
29
+
30
+ # PhiC31 — DISCLOSED DATA GAP (reported, not fabricated): PhiC31 is the integrase with the best-documented human
31
+ # pseudo-attP set (~19 sites, Chalberg et al. 2006), which would make the strongest semi-validation benchmark.
32
+ # Its exact minimal attB/attP arm sequences and the pseudo-attP genomic coordinates could not be verified from an
33
+ # open, citable source in this build (the central crossover is a documented TT dinucleotide; the minimal attB is
34
+ # ~34 bp). Rather than fabricate the arm sequence or the pseudosite coordinates, PhiC31 is NOT encoded here; the
35
+ # integrase scan abstains for PhiC31 with this disclosure. Encoding the verified PhiC31 att + the Chalberg-2006
36
+ # pseudo-attP table is the follow-up that would upgrade O-G2 from "stated" to a sealed recall benchmark.
37
+ phic31_disclosure:
38
+ status: "not_encoded_data_gated"
39
+ documented_fact: "PhiC31 has ~19 characterised human pseudo-attP sites (a promiscuous serine integrase)."
40
+ central_dinucleotide: "TT"
41
+ minimal_attB_length_bp: 34
42
+ reference: "Chalberg et al., J Mol Biol 2006 (PhiC31 human pseudo-attP mapping); Thyagarajan et al., Mol Cell Biol 2001"
43
+ doi: "10.1016/j.jmb.2005.11.108"
@@ -90,3 +90,29 @@ candidate ENUMERATION needs the on-VM Cas-OFFinder/genome scan; this benchmark c
90
90
  candidates. Bridge/integrase off-target is data-thin: there is **no published genome-wide unbiased off-target assay
91
91
  or predictor for bridge recombinases** (verified), and the large-serine-integrase assays (Cryptic-seq/HIDE-seq) and
92
92
  predictor (IntQuery) are recent single-company preprints with no public weights.
93
+
94
+ ---
95
+
96
+ ## v7.2 (PEN-OFFTGT v2) — per-mechanism ground-truth status (O-WS8 / gate O-G3)
97
+
98
+ Stage E is now a genome-wide **finder** (Cas-OFFinder over GRCh38; heavy scan on the VM, cached coordinates
99
+ replayed by the app). Each writer class applies the correct off-target mechanism and carries a **truthful
100
+ validation status** — a sealed benchmark where ground truth exists, an explicit **no-genome-wide-ground-truth
101
+ disclosure** where it does not. No mechanism is ever presented as validated where the data cannot support it.
102
+
103
+ | Writer class | Mechanism | Enumeration | Status | Ground truth / benchmark |
104
+ |---|---|---|---|---|
105
+ | **Nuclease** (SpCas9/SaCas9/Cas12a) | mismatch-tolerant cleavage at protospacer+PAM | Cas-OFFinder genome scan | ✅ **validated** | 4-assay Off-Target-Bench (above) + **O-G1**: enumeration recovers 100% of EMX1's documented GUIDE-seq off-targets ≤5 mm |
106
+ | **Serine integrase** (Bxb1) | recombination at genomic pseudo-attP | fixed-sequence att-window scan | 🟡 **semi-validated** | documented pseudosites are partial ground truth; Bxb1 att verified (FlyBase FBto0000359 / Ghosh 2003, `10.1016/S1097-2765(03)00444-1`); Bxb1 is highly specific (few genomic pseudo-attP) |
107
+ | **Serine integrase** (PhiC31) | — | — | 🟡 semi-validated | **DISCLOSED DATA GAP**: PhiC31 has ~19 documented human pseudo-attP (Chalberg 2006, `10.1016/j.jmb.2005.11.108`) — the strongest integrase benchmark — but its exact att arm / pseudosite sequences were not verifiable from an open source in this build, so PhiC31 is **not encoded and abstains** rather than fabricate. Encoding it is the follow-up that upgrades O-G2 to a sealed recall benchmark. |
108
+ | **Bridge** (IS110/IS621) | recombination at bridge-RNA target-loop matches | core-seeded genome scan (pysam) | 🔵 **mechanism-based, unvalidated** | **NO genome-wide unbiased CELLULAR off-target assay exists** (technology ~2024). The mismatch-tolerance RANKER is validated on the measured Perry-2025 in-vitro DMS specificity (held-out ranking AUROC 0.88), but genomic recovery is unvalidated. |
109
+ | **CAST** (ShCAST V-K / VchCAST I-F) | guide-directed integration + guide-independent untargeted transposition | spacer scan + per-system untargeted background | 🔵 **mechanism-based, unvalidated** | untargeted-transposition rates documented per system (`data/curated/cast_systems.yaml`): ShCAST high/AT-biased (Strecker 2019 `10.1126/science.aax9181`; Science 2024 `10.1126/science.adj8543`), VchCAST >95–99% on-target (Klompe 2019 `10.1038/s41586-019-1323-z`; Vo 2021 `10.1038/s41587-020-00745-y`). No genome-wide cellular assay for human-cell CAST. |
110
+ | **PASTE / (ee)PASSIGE** | Cas9-nickase off-target + integrase pseudo-attP | compose(nuclease, integrase) | composite (✅ nickase + 🟡 integrase) | inherits the nuclease benchmark for the nickase and the integrase status for the installed att; recommends BOTH a nuclease assay AND an integrase assay |
111
+
112
+ **O-G3 satisfied:** every mechanism has either a sealed benchmark (nuclease) or an explicit no-ground-truth /
113
+ data-gap disclosure (integrase-PhiC31, bridge, CAST) — never a fabricated metric. The bridge and CAST paths are
114
+ **hard-locked** to 🔵 unvalidated in code.
115
+
116
+ **Honest limits (v7.2):** enumeration recall depends on the mismatch tolerance (≤5 mm nuclease, ≤8 mm integrase
117
+ att window); very divergent off-targets can be missed (a limitation shared with CRISPOR). DNA/RNA bulges are not
118
+ enumerated in v2.0 (substitutions only). The engine nominates and ranks; it does not clear a design.
@@ -1,2 +1,2 @@
1
1
  """PEN-STACK v3.0 - open infrastructure for genome writing."""
2
- __version__ = "7.1.8"
2
+ __version__ = "7.2.0"
@@ -156,14 +156,17 @@ def immune_profile(design: dict) -> dict:
156
156
 
157
157
  @mcp.tool()
158
158
  def offtarget_scan(writer_family: str, guide: str | None = None, candidate_sites: list | None = None,
159
- sequence: str | None = None, assay: str = "guideseq") -> dict:
160
- """v6.10 PEN-OFFTGT cross-family off-target NOMINATION (NOT a clearance). Ranks candidate sites with a
161
- real-data mismatch-calibrated risk band + the real CRISOT learned score (nuclease), a pseudo-attB scan
162
- (integrase), or the Perry-DMS pseudosite engine (bridge); ships the validation assay that would confirm
163
- each candidate. Abstains without inputs; never fabricates sites."""
159
+ sequence: str | None = None, assay: str = "guideseq", enzyme: str | None = None,
160
+ max_mismatch: int = 5) -> dict:
161
+ """PEN-OFFTGT v2 genome-wide, per-mechanism off-target FINDER (NOT a clearance). For a nuclease guide it
162
+ enumerates the genome-wide off-target set over GRCh38 (Cas-OFFinder, cached) + real CRISOT + risk + chromatin
163
+ (validated); serine integrase = genome-wide pseudo-attP scan (semi-validated); bridge = DMS-scored scan
164
+ (mechanism-based, unvalidated); CAST = guide-directed + untargeted-transposition background (unvalidated);
165
+ PASTE = nuclease + integrase composition. Each carries a truthful status label + the confirming assay.
166
+ Abstains for a novel input (VM scan); never fabricates sites."""
164
167
  from pen_stack.wgenome.offtarget_predict import nominate_offtargets
165
168
  return nominate_offtargets(writer_family, guide=guide, candidate_sites=candidate_sites,
166
- sequence=sequence, assay=assay)
169
+ sequence=sequence, assay=assay, enzyme=enzyme, max_mismatch=max_mismatch)
167
170
 
168
171
 
169
172
  @mcp.tool()
@@ -50,11 +50,13 @@ _TOOLS = [
50
50
  "efficiency w/ conformal interval + auto-designed guide/att (v6.8 PEN-WRITER)",
51
51
  "input": "write request (write-type, cargo, cell type, optional target/donor seq)", "output": "WriterRanking",
52
52
  "entrypoint": "pen_stack.atlas.writer_recommend.recommend_writers", "fabricates": False},
53
- {"name": "nominate_offtargets", "summary": "cross-writer-family off-target NOMINATION: rank candidate sites "
54
- "with a real-data mismatch-calibrated risk band + the real CRISOT learned score (nuclease), pseudo-attB "
55
- "scan (integrase), Perry-DMS pseudosite engine (bridge); nomination is NOT a clearance (v6.10 PEN-OFFTGT)",
56
- "input": "writer family + guide/candidate sites (nuclease) or locus sequence (integrase)",
57
- "output": "ranked off-target candidates + calibrated risk + recommended validation assay",
53
+ {"name": "nominate_offtargets", "summary": "genome-wide, per-mechanism off-target FINDER (v7.2 PEN-OFFTGT v2): "
54
+ "enumerates the off-target set over GRCh38 (Cas-OFFinder) and applies the correct mechanism per writer class "
55
+ "- nuclease cleavage (validated: CRISOT + risk + chromatin), integrase pseudo-attP (semi-validated), bridge "
56
+ "target-specificity (unvalidated), CAST guide + untargeted transposition (unvalidated), PASTE composition - "
57
+ "each with a truthful status label; nomination is NOT a clearance",
58
+ "input": "writer family + guide/target (enumerated genome-wide) or supplied candidate sites",
59
+ "output": "genome-wide ranked off-target candidates + per-mechanism status + calibrated risk + validation assay",
58
60
  "entrypoint": "pen_stack.wgenome.offtarget_predict.nominate_offtargets", "fabricates": False},
59
61
  {"name": "recommend_delivery", "summary": "cross-modality delivery recommender: rank vehicles by cargo-form + "
60
62
  "safety<->efficacy + a grounded serotype->tissue tropism prior (approved therapies) + the learned FLIP-AAV "
@@ -374,15 +374,19 @@ def immune_endpoint(design: dict):
374
374
 
375
375
  @app.post("/offtarget", tags=["v6.10 off-target"])
376
376
  def offtarget_endpoint(req: dict):
377
- """v6.10 PEN-OFFTGT cross-family off-target NOMINATION (NOT a clearance). Body:
378
- {writer_family, guide?, candidate_sites?, sequence?, accessibility?, assay?}. Returns ranked candidate
379
- off-targets with a real-data mismatch-calibrated risk band + the recommended validation assay; abstains
380
- without inputs and never fabricates sites."""
377
+ """PEN-OFFTGT cross-family off-target NOMINATION (NOT a clearance). Body:
378
+ {writer_family, guide?, enzyme?, max_mismatch?, candidate_sites?, sequence?, accessibility?, assay?}.
379
+
380
+ v7.2 (finder): for a nuclease guide with NO ``candidate_sites``, enumerates the genome-wide off-target set
381
+ over GRCh38 (Cas-OFFinder, replayed from the committed cache) and ranks it by the real CRISOT-Score +
382
+ mismatch-calibrated risk + chromatin annotation - the CRISPOR-like default. Supplying ``candidate_sites``
383
+ keeps the v6.10 score-my-candidates path. Abstains (never fabricates) for a novel guide with no VM scan."""
381
384
  from pen_stack.wgenome.offtarget_predict import nominate_offtargets
382
385
  return nominate_offtargets(
383
386
  req.get("writer_family", ""), guide=req.get("guide"), candidate_sites=req.get("candidate_sites"),
384
387
  sequence=req.get("sequence"), accessibility=req.get("accessibility"),
385
- target_core=req.get("target_core"), assay=req.get("assay", "guideseq"))
388
+ target_core=req.get("target_core"), assay=req.get("assay", "guideseq"),
389
+ enzyme=req.get("enzyme"), max_mismatch=int(req.get("max_mismatch", 5)))
386
390
 
387
391
 
388
392
  @app.get("/offtarget/assay", tags=["v6.10 off-target"])
@@ -392,6 +396,20 @@ def offtarget_assay_endpoint(writer_family: str):
392
396
  return recommend_assay(writer_family)
393
397
 
394
398
 
399
+ @app.get("/offtarget/enumerated", tags=["v6.10 off-target"])
400
+ def offtarget_enumerated_endpoint():
401
+ """v7.2: the guides whose genome-wide off-target enumeration is CACHED (so the finder works here without a VM
402
+ scan). A novel guide abstains (its scan runs on the VM). Enumerated coordinates are public-genome facts."""
403
+ from pen_stack.wgenome.offtarget_data import CANONICAL_GUIDES
404
+ from pen_stack.wgenome.offtarget_enumerate import enumerated_guides
405
+ cached = enumerated_guides()
406
+ seq2name = {v: k for k, v in CANONICAL_GUIDES.items()}
407
+ return {"enzyme": "SpCas9",
408
+ "guides": [{"guide": g, "name": seq2name.get(g, g), "enzyme": e} for e, g in cached],
409
+ "note": ("genome-wide enumeration for these guides is replayed from the committed GRCh38 Cas-OFFinder "
410
+ "cache; a novel guide requires an on-VM scan (the finder abstains rather than fabricate).")}
411
+
412
+
395
413
  @app.get("/campaign", tags=["v7.0 closed-loop"])
396
414
  def campaign_endpoint():
397
415
  """v7.0 Stage J: the validation-campaign engine. Returns the expression-validation campaign: the next batch of
@@ -29,7 +29,7 @@ def recommend_assay(writer_family: str) -> dict:
29
29
  "in the target cell type (GUIDE-seq), chromatin masks a fraction of in vitro sites",
30
30
  "available": True,
31
31
  "note": "nomination ranks CANDIDATES; an empirical assay is required for clearance."}
32
- if "integrase" in fam or "paste" in fam or "passige" in fam or "bxb1" in fam or "phic31" in fam:
32
+ if ("integrase" in fam or "bxb1" in fam or "phic31" in fam) and "paste" not in fam and "passige" not in fam:
33
33
  return {"family": writer_family, "writer_class": "large serine integrase",
34
34
  "recommended": [{"assay": "Cryptic-seq / HIDE-seq", "setting": "unbiased LSI off-target discovery",
35
35
  "doi": "10.1101/2024.08.23.609471",
@@ -39,6 +39,29 @@ def recommend_assay(writer_family: str) -> dict:
39
39
  "quantitative prediction (IntQuery) is paper-only (no public weights)",
40
40
  "available": True,
41
41
  "note": "LSI off-target assays are recent preprints; coverage is single-company / largely Bxb1."}
42
+ if "paste" in fam or "passige" in fam:
43
+ return {"family": writer_family, "writer_class": "prime-editing integrase (PASTE / PASSIGE)",
44
+ "recommended": [
45
+ {"assay": "GUIDE-seq / CHANGE-seq", "setting": "nuclease (nickase) off-target", "doi": "10.1038/nbt.3117",
46
+ "use": "confirm the Cas9-nickase off-targets of the pegRNA spacer"},
47
+ {"assay": "Cryptic-seq / HIDE-seq", "setting": "integrase off-target", "doi": "10.1101/2024.08.23.609471",
48
+ "use": "confirm the integrase pseudo-attP sites of the installed att"}],
49
+ "strategy": "PASTE off-target is composite — run BOTH a nuclease assay (for the nickase) AND an "
50
+ "integrase assay (for the installed att); the two components are independent.",
51
+ "available": True,
52
+ "note": "two independent off-target mechanisms; neither assay alone clears a PASTE design."}
53
+ if "cast" in fam or "cas12k" in fam or "shcast" in fam or "vchcast" in fam or "evocast" in fam:
54
+ return {"family": writer_family, "writer_class": "CRISPR-associated transposase (CAST)",
55
+ "recommended": [{"assay": "transposon insertion-site sequencing", "setting": "unbiased integration mapping",
56
+ "doi": "10.1126/science.aax9181",
57
+ "use": "map genome-wide integration sites, including guide-INDEPENDENT untargeted "
58
+ "transposition (the dominant off-target mode for Type V-K, e.g. ShCAST)"}],
59
+ "strategy": "sequence integration sites to capture BOTH guide-directed off-targets and the "
60
+ "guide-independent untargeted-transposition background (Type V-K high; Type I-F low)",
61
+ "available": True,
62
+ "note": "KNOWN GAP: no genome-wide unbiased CELLULAR off-target assay is standardised for human-cell "
63
+ "CAST; untargeted-transposition rates are characterised in bacteria/biochemically. "
64
+ "Mechanism-based, unvalidated."}
42
65
  if "bridge" in fam or "is110" in fam or "is621" in fam or "seek" in fam or "iscro4" in fam:
43
66
  return {"family": writer_family, "writer_class": "bridge recombinase (IS110/IS621 RNA-guided)",
44
67
  "recommended": [{"assay": "targeted amplicon / capture sequencing at nominated pseudosites",
@@ -0,0 +1,47 @@
1
+ """Bridge-recombinase (IS110 / IS621) off-target path (PEN-OFFTGT v2, O-WS4).
2
+
3
+ Bridge recombinases are RNA-guided: the bridge RNA's target-binding loop base-pairs with a bipartite ~14-nt
4
+ genomic target (central CT). Off-target = genomic sites matching that target with tolerated mismatches. This
5
+ wraps the existing genome-wide scanner (`pen_stack.bridge.offtarget`), which seeds on the central core and scores
6
+ each pseudosite by the **measured Perry-2025 DMS specificity landscape** (which target mismatches the recombinase
7
+ tolerates) — a held-out ranking AUROC of 0.88 on that in-vitro DMS data.
8
+
9
+ **Status: mechanism_based_unvalidated (hard-locked).** Crucially, the DMS ranker being validated on *in-vitro
10
+ specificity* does NOT make the *genomic off-target predictions* validated: there is **no published genome-wide
11
+ unbiased CELLULAR off-target assay** for bridge recombinases (the technology is ~2024), so the genomic pseudosite
12
+ calls cannot be validated. The honest report is: a mechanism-based scan with a DMS-grounded mismatch-tolerance
13
+ model, labelled unvalidated, with the no-genome-wide-assay disclosure. Confirm by targeted amplicon /
14
+ integration-site sequencing. Never claims validation.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ _STATUS = "mechanism_based_unvalidated"
19
+ _DISCLOSURE = (
20
+ "NO published genome-wide unbiased CELLULAR off-target assay exists for bridge recombinases (IS110/IS621; the "
21
+ "technology is ~2024). The pseudosite RANKER is validated on the measured Perry-2025 in-vitro DMS specificity "
22
+ "landscape (held-out ranking AUROC 0.88), but genomic recovery is NOT validated. Treat nominations as "
23
+ "high-uncertainty, mechanism-based candidates — never a clearance."
24
+ )
25
+
26
+
27
+ def nominate_bridge(target_core: str | None = None, writer_family: str = "bridge_IS110",
28
+ fasta=None, chroms: list[str] | None = None, top: int = 20) -> dict:
29
+ """Genome-wide bridge off-target scan for a bridge-RNA target core, via the DMS-scored engine. Returns the
30
+ ranked pseudosites when a genome + target core are supplied (VM), else reports the engine is ready and how to
31
+ run the scan. Always carries the hard-locked unvalidated status + the no-genome-wide-assay disclosure."""
32
+ from pen_stack.bridge.offtarget import predict_offtargets
33
+ fam = "seek_IS1111" if ("seek" in (writer_family or "").lower() or "is1111" in (writer_family or "").lower()) \
34
+ else "bridge_IS110"
35
+ res = predict_offtargets(fam, target_core=target_core, fasta=fasta, chroms=chroms, top=top)
36
+ return {"family": "bridge", "writer_family": fam, "status": _STATUS,
37
+ "ranker": "Perry-2025 DMS specificity landscape (measured; held-out ranking AUROC 0.88)",
38
+ "no_ground_truth_disclosure": _DISCLOSURE,
39
+ "confirm_assay": "targeted amplicon / integration-site sequencing at nominated pseudosites",
40
+ "available": res.get("status") == "scanned",
41
+ "abstain": not (res.get("status") == "scanned"),
42
+ "engine": res,
43
+ "method": ("seed on the central core (CT), verify the bipartite ~14-nt target with tolerated "
44
+ "mismatches, score by the measured DMS specificity (position + substitution identity)"),
45
+ "honesty": ("mechanism-based CANDIDATES with a DMS-grounded mismatch-tolerance model; NOT a validated "
46
+ "genome-wide predictor (no cellular assay exists) and NOT a clearance."),
47
+ "nomination_is_not_clearance": True}
@@ -0,0 +1,97 @@
1
+ """CAST (CRISPR-associated transposase) off-target path (PEN-OFFTGT v2, O-WS5) — NEW.
2
+
3
+ CAST off-target is two mechanisms, and their balance is system-dependent:
4
+ * **guide-directed** — integration at genomic sites matching the crRNA spacer (a sequence-match scan; the
5
+ transposon inserts a fixed distance downstream of the protospacer). Enumerated genome-wide on the VM (or
6
+ replayed from cache); abstains for a novel spacer.
7
+ * **guide-INDEPENDENT untargeted transposition** — the Tn7-like machinery (TnsB/TnsC/TniQ) inserting without the
8
+ CRISPR effector. This is the DISTINCTIVE, dominant off-target mode for **Type V-K** (ShCAST) and is largely
9
+ absent in **Type I-F** (VchCAST). It is reported as a per-system documented property (`cast_systems.yaml`),
10
+ with DOIs — NOT a genome-wide prediction.
11
+
12
+ Status: **mechanism_based_unvalidated** (no genome-wide unbiased CELLULAR off-target assay exists for CAST). The
13
+ confirming assay is transposon insertion-site sequencing. Never fabricates a validated metric.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ from functools import lru_cache
18
+
19
+ from pen_stack.wgenome.offtarget_enumerate import DEFAULT_MAX_MISMATCH, enumerate_motif
20
+
21
+ _STATUS = "mechanism_based_unvalidated"
22
+ _ALIAS = {"shcast": "ShCAST", "vchcast": "VchCAST", "evocast": "evoCAST",
23
+ "cast": "ShCAST", "cast_vk": "ShCAST", "cast_v-k": "ShCAST", "cast_if": "VchCAST",
24
+ "type_v-k": "ShCAST", "type_i-f": "VchCAST", "cas12k": "ShCAST"}
25
+
26
+
27
+ @lru_cache(maxsize=1)
28
+ def cast_systems() -> dict:
29
+ """The curated CAST system table (per-system untargeted-transposition properties + DOIs), or {} when absent."""
30
+ try:
31
+ import yaml
32
+
33
+ from pen_stack._resources import resource
34
+ return yaml.safe_load(resource("data/curated/cast_systems.yaml").read_text(encoding="utf-8")) or {}
35
+ except Exception: # noqa: BLE001
36
+ return {}
37
+
38
+
39
+ def resolve_cast_system(name: str) -> str | None:
40
+ sysd = cast_systems().get("systems", {})
41
+ n = (name or "").strip()
42
+ if n in sysd:
43
+ return n
44
+ return _ALIAS.get(n.lower())
45
+
46
+
47
+ def nominate_cast(system: str = "ShCAST", spacer: str | None = None,
48
+ max_mismatch: int = DEFAULT_MAX_MISMATCH) -> dict:
49
+ """CAST off-target nomination: the guide-independent untargeted-transposition background (per-system documented
50
+ property) PLUS guide-directed enumeration for a supplied spacer (genome-wide, replayed/abstained). Never
51
+ fabricates sites or a validated metric; status is mechanism_based_unvalidated."""
52
+ tbl = cast_systems()
53
+ key = resolve_cast_system(system) or "ShCAST"
54
+ rec = (tbl.get("systems") or {}).get(key, {})
55
+ if not rec:
56
+ return {"family": "cast", "available": False, "abstain": True, "status": _STATUS,
57
+ "note": f"no curated CAST system {system!r}; known: {sorted((tbl.get('systems') or {}))}"}
58
+
59
+ untargeted = {
60
+ "mode": "guide-independent untargeted transposition (TnsB/TnsC/TniQ, no CRISPR effector)",
61
+ "tier": rec.get("untargeted_transposition"),
62
+ "guide_independent": rec.get("guide_independent"),
63
+ "at_biased": rec.get("at_biased_untargeted"),
64
+ "cast_type": rec.get("cast_type"),
65
+ "fidelity_note": rec.get("on_target_fidelity_note"),
66
+ "dois": rec.get("dois", []),
67
+ "note": ("the DISTINCTIVE CAST off-target mode: Type V-K (ShCAST) shows HIGH guide-independent, AT-biased "
68
+ "untargeted transposition; Type I-F (VchCAST) is high-fidelity (low untargeted). A documented "
69
+ "per-system property, NOT a genome-wide prediction."),
70
+ }
71
+
72
+ # guide-directed: a spacer-match genome scan (enumerated on the VM; replayed from cache or abstains).
73
+ guided = None
74
+ if spacer:
75
+ enum = enumerate_motif(f"CAST_{key}_{spacer.upper()}")
76
+ if enum.get("available"):
77
+ sites = enum["sites"]
78
+ n_on = sum(1 for s in sites if s["n_mismatch"] == 0)
79
+ guided = {"mode": "guide-directed (crRNA spacer match)", "available": True,
80
+ "n_sites_genome_wide": len(sites), "n_on_target": n_on,
81
+ "n_offtargets": len(sites) - n_on, "source": enum["source"],
82
+ "sites": sorted(sites, key=lambda s: s["n_mismatch"])[:50],
83
+ "note": "genomic sites matching the crRNA spacer; the transposon inserts a fixed distance "
84
+ "downstream. Ranked by mismatch (no CRISOT — that is a Cas9-nuclease model)."}
85
+ else:
86
+ guided = {"mode": "guide-directed (crRNA spacer match)", "available": False, "abstain": True,
87
+ "note": enum["note"], "cached_motifs": enum.get("cached_motifs", [])}
88
+
89
+ return {"family": "cast", "system": key, "cast_type": rec.get("cast_type"), "available": True, "abstain": False,
90
+ "status": _STATUS, "untargeted_background": untargeted, "guide_directed": guided,
91
+ "confirm_assay": tbl.get("confirm_assay"),
92
+ "method": ("CAST off-target = guide-directed spacer-match enumeration (genome-wide, VM) + the "
93
+ "guide-independent untargeted-transposition background (documented per-system property). "
94
+ "No genome-wide unbiased cellular assay exists -> mechanism-based, unvalidated."),
95
+ "honesty": "CANDIDATES + a documented untargeted-transposition risk; NOT a clearance and NOT a "
96
+ "validated genome-wide predictor. Confirm by transposon insertion-site sequencing.",
97
+ "nomination_is_not_clearance": True}