pen-stack 7.1.3__tar.gz → 7.1.5__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 (567) hide show
  1. {pen_stack-7.1.3 → pen_stack-7.1.5}/CHANGELOG.md +52 -0
  2. {pen_stack-7.1.3 → pen_stack-7.1.5}/CITATION.cff +2 -2
  3. {pen_stack-7.1.3 → pen_stack-7.1.5}/PKG-INFO +1 -1
  4. pen_stack-7.1.5/configs/rules/compliance.yaml +44 -0
  5. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/write_types.yaml +7 -7
  6. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/rule_spec.md +2 -1
  7. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/__init__.py +1 -1
  8. pen_stack-7.1.5/pen_stack/planner/chromosome.py +106 -0
  9. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rules/evaluators.py +90 -0
  10. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rules/loader.py +1 -1
  11. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rules/schema.py +1 -1
  12. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/server/api.py +17 -0
  13. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/verify/proof.py +7 -0
  14. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/verify/service.py +12 -0
  15. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack.egg-info/PKG-INFO +1 -1
  16. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack.egg-info/SOURCES.txt +2 -0
  17. {pen_stack-7.1.3 → pen_stack-7.1.5}/pyproject.toml +1 -1
  18. {pen_stack-7.1.3 → pen_stack-7.1.5}/LICENSE +0 -0
  19. {pen_stack-7.1.3 → pen_stack-7.1.5}/MANIFEST.in +0 -0
  20. {pen_stack-7.1.3 → pen_stack-7.1.5}/README.md +0 -0
  21. {pen_stack-7.1.3 → pen_stack-7.1.5}/bench/run.py +0 -0
  22. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/chat_grounding/README.md +0 -0
  23. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/chat_grounding/SHA256SUMS +0 -0
  24. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/chat_headtohead/README.md +0 -0
  25. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/chat_routing/README.md +0 -0
  26. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/chat_routing/SHA256SUMS +0 -0
  27. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/chat_safety/README.md +0 -0
  28. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/chat_safety/SHA256SUMS +0 -0
  29. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/delivery/SHA256SUMS +0 -0
  30. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
  31. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/genome_writing_bench/README.md +0 -0
  32. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
  33. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  34. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
  35. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/genome_writing_challenge/README.md +0 -0
  36. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
  37. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/loop/SHA256SUMS +0 -0
  38. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/offtarget/SHA256SUMS +0 -0
  39. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/oracle/SHA256SUMS +0 -0
  40. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/position_effect/README.md +0 -0
  41. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/position_effect/SHA256SUMS +0 -0
  42. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/verify/SHA256SUMS +0 -0
  43. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/writer_efficiency/README.md +0 -0
  44. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/writer_efficiency/SHA256SUMS +0 -0
  45. {pen_stack-7.1.3 → pen_stack-7.1.5}/benchmarks/writespec/SHA256SUMS +0 -0
  46. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/aav_serotype_tropism.yaml +0 -0
  47. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/antipeg.yaml +0 -0
  48. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/atlas_families.yaml +0 -0
  49. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/bridge_offtarget_profile.yaml +0 -0
  50. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/calibration/preexisting_nab_independent.yaml +0 -0
  51. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/capsid_epitope_oracle.yaml +0 -0
  52. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/capsid_sequences.fasta +0 -0
  53. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/cargo_polish.yaml +0 -0
  54. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/cell_types.yaml +0 -0
  55. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/datasets.yaml +0 -0
  56. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/delivery_constraints.yaml +0 -0
  57. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/delivery_rules.yaml +0 -0
  58. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/delivery_vehicles.yaml +0 -0
  59. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/expression/modifiers.yaml +0 -0
  60. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/expression/promoters.yaml +0 -0
  61. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/gates_v3.yaml +0 -0
  62. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/genotoxicity_oracle.yaml +0 -0
  63. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/gsh_validated_heldout.yaml +0 -0
  64. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/intent_weights.yaml +0 -0
  65. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/known_unknowns.yaml +0 -0
  66. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/llm.yaml +0 -0
  67. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/metric_guide.yaml +0 -0
  68. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/mhc_epitope_oracle.yaml +0 -0
  69. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/monitor_queries.yaml +0 -0
  70. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/oracles/execution.yaml +0 -0
  71. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/oracles/reliability.yaml +0 -0
  72. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/oracles/scope_cards.yaml +0 -0
  73. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/rules/delivery.yaml +0 -0
  74. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/rules/fold.yaml +0 -0
  75. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/rules/multiplex.yaml +0 -0
  76. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/rules/payload.yaml +0 -0
  77. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/rules/reachability.yaml +0 -0
  78. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/safety/hazard_registry.yaml +0 -0
  79. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/safety/policy.yaml +0 -0
  80. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/safety/probes.yaml +0 -0
  81. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/score_axes.yaml +0 -0
  82. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/seroprevalence.yaml +0 -0
  83. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/target_sites.yaml +0 -0
  84. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/universe_crosswalk.yaml +0 -0
  85. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/writer_sequences.fasta +0 -0
  86. {pen_stack-7.1.3 → pen_stack-7.1.5}/configs/wtkb_curated.yaml +0 -0
  87. {pen_stack-7.1.3 → pen_stack-7.1.5}/data/curated/bridge_offtarget_energetics.json +0 -0
  88. {pen_stack-7.1.3 → pen_stack-7.1.5}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  89. {pen_stack-7.1.3 → pen_stack-7.1.5}/data/curated/gene_coords.parquet +0 -0
  90. {pen_stack-7.1.3 → pen_stack-7.1.5}/data/curated/unified_editor_universe.parquet +0 -0
  91. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/BACKLOG.md +0 -0
  92. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/DEPLOY.md +0 -0
  93. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/INFRA.md +0 -0
  94. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/MCP.md +0 -0
  95. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/RELEASING.md +0 -0
  96. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/REPRO.md +0 -0
  97. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/STABILITY.md +0 -0
  98. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/agent.md +0 -0
  99. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/alphagenome_feasibility.md +0 -0
  100. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/autonomy.md +0 -0
  101. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/benchmark_circularity.md +0 -0
  102. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/biosecurity.md +0 -0
  103. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/build_interface.md +0 -0
  104. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/cards/atlas.md +0 -0
  105. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/cards/durability.md +0 -0
  106. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/cards/offtarget_data.md +0 -0
  107. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/cards/position_effect_data.md +0 -0
  108. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/cards/safety.md +0 -0
  109. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/cards/writer_efficiency_data.md +0 -0
  110. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/challenge.md +0 -0
  111. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/closed_loop.md +0 -0
  112. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/co_scientist.md +0 -0
  113. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/co_scientist_loop.md +0 -0
  114. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/delivery.md +0 -0
  115. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/delivery_immunology.md +0 -0
  116. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/delivery_recommender.md +0 -0
  117. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/digital_twin.md +0 -0
  118. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/dissemination.md +0 -0
  119. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/environment.md +0 -0
  120. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/experiment_design.md +0 -0
  121. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/generative_design.md +0 -0
  122. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/immune_profiler.md +0 -0
  123. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/index.md +0 -0
  124. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/integrations.md +0 -0
  125. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/live_oracles.md +0 -0
  126. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/mechanistic_constraints.md +0 -0
  127. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/offtarget.md +0 -0
  128. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/oracle_mesh.md +0 -0
  129. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/oracles.md +0 -0
  130. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/position_effect.md +0 -0
  131. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/positioning.md +0 -0
  132. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/private_data_formats.md +0 -0
  133. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/quickstart.md +0 -0
  134. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/responsible_use.md +0 -0
  135. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/rules.md +0 -0
  136. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/scope.md +0 -0
  137. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/scorecard.md +0 -0
  138. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/tpe_bench.md +0 -0
  139. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/tutorials/compare-families.md +0 -0
  140. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/tutorials/score-deliverability.md +0 -0
  141. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/tutorials/where-can-i-write.md +0 -0
  142. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  143. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/uncertainty.md +0 -0
  144. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/verify.md +0 -0
  145. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/verify_service.md +0 -0
  146. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/world_model.md +0 -0
  147. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/writer_efficiency.md +0 -0
  148. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/writer_verification.md +0 -0
  149. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/writespec_bench.md +0 -0
  150. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/writespec_profile.md +0 -0
  151. {pen_stack-7.1.3 → pen_stack-7.1.5}/docs/wtkb.md +0 -0
  152. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/_resources.py +0 -0
  153. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/active/__init__.py +0 -0
  154. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/active/acquire.py +0 -0
  155. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/active/brains.py +0 -0
  156. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/active/campaign.py +0 -0
  157. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/active/design.py +0 -0
  158. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/active/validate.py +0 -0
  159. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/adapt/__init__.py +0 -0
  160. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/adapt/finetune.py +0 -0
  161. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/adapt/ingest.py +0 -0
  162. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/adapt/pipeline.py +0 -0
  163. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/adapt/recalibrate.py +0 -0
  164. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/adapt/report.py +0 -0
  165. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/__init__.py +0 -0
  166. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/cite.py +0 -0
  167. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/co_scientist.py +0 -0
  168. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/epistemic.py +0 -0
  169. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/guardrails.py +0 -0
  170. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/mcp_server.py +0 -0
  171. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/orchestrator.py +0 -0
  172. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/orchestrator_live.py +0 -0
  173. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/pen_agent.py +0 -0
  174. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/scope.py +0 -0
  175. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/agent/tools.py +0 -0
  176. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/api/__init__.py +0 -0
  177. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/api/manifest.py +0 -0
  178. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/__init__.py +0 -0
  179. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/build_wtkb.py +0 -0
  180. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/crosslink.py +0 -0
  181. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/expand.py +0 -0
  182. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/guide_design.py +0 -0
  183. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/schema.py +0 -0
  184. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/scorecard.py +0 -0
  185. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/universe.py +0 -0
  186. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/variant_propose.py +0 -0
  187. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/writer_efficiency.py +0 -0
  188. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/writer_predict.py +0 -0
  189. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/writer_recommend.py +0 -0
  190. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/atlas/writer_verify.py +0 -0
  191. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/__init__.py +0 -0
  192. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/activity.py +0 -0
  193. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/cli.py +0 -0
  194. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/fold_qc.py +0 -0
  195. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/guide_qc.py +0 -0
  196. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/ingest.py +0 -0
  197. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/offtarget.py +0 -0
  198. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/offtarget_energetics.py +0 -0
  199. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/ortholog_screen.py +0 -0
  200. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/bridge/pipeline.py +0 -0
  201. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/build/__init__.py +0 -0
  202. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/build/cloudlab.py +0 -0
  203. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/build/ingest.py +0 -0
  204. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/build/protocol.py +0 -0
  205. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/build/simlab.py +0 -0
  206. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/cli.py +0 -0
  207. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/data/__init__.py +0 -0
  208. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/data/encode.py +0 -0
  209. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/data/genome.py +0 -0
  210. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/data/ingest_chromatin.py +0 -0
  211. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/data/ingest_integration.py +0 -0
  212. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/data/ingest_safety_annot.py +0 -0
  213. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/data/ingest_trip.py +0 -0
  214. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/design/__init__.py +0 -0
  215. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/design/capsid_generate.py +0 -0
  216. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/design/generate.py +0 -0
  217. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/design/pareto.py +0 -0
  218. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/design/space.py +0 -0
  219. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/design/writer_variants.py +0 -0
  220. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/env/__init__.py +0 -0
  221. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/env/genome_writing_env.py +0 -0
  222. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/env/policies.py +0 -0
  223. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/graph/__init__.py +0 -0
  224. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/graph/build.py +0 -0
  225. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/graph/cell_types.py +0 -0
  226. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/graph/ingest.py +0 -0
  227. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/graph/query.py +0 -0
  228. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/graph/schema.py +0 -0
  229. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/loop/__init__.py +0 -0
  230. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/loop/continual.py +0 -0
  231. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/loop/cycle.py +0 -0
  232. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/loop/drift.py +0 -0
  233. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/mech/__init__.py +0 -0
  234. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/mech/classify_atlas.py +0 -0
  235. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/mech/whitelist.py +0 -0
  236. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/monitor/__init__.py +0 -0
  237. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/monitor/europepmc.py +0 -0
  238. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/monitor/run.py +0 -0
  239. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/monitor/triage.py +0 -0
  240. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/__init__.py +0 -0
  241. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/affinity.py +0 -0
  242. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/cache.py +0 -0
  243. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/energetics.py +0 -0
  244. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/genome.py +0 -0
  245. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/protein_design.py +0 -0
  246. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/reliability.py +0 -0
  247. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/rna.py +0 -0
  248. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/schema.py +0 -0
  249. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/status.py +0 -0
  250. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/structure.py +0 -0
  251. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/structure_run.py +0 -0
  252. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/oracles/vcell.py +0 -0
  253. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/__init__.py +0 -0
  254. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/ada_risk.py +0 -0
  255. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/antipeg_oracle.py +0 -0
  256. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
  257. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/cargo.py +0 -0
  258. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/cargo_polish.py +0 -0
  259. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/delivery.py +0 -0
  260. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/delivery_constraints.py +0 -0
  261. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/delivery_immune.py +0 -0
  262. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/delivery_immunology.py +0 -0
  263. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/delivery_predict.py +0 -0
  264. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/delivery_vehicles.py +0 -0
  265. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/genotoxicity_oracle.py +0 -0
  266. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/immune_mhc2.py +0 -0
  267. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/immune_profile.py +0 -0
  268. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/innate_sensing.py +0 -0
  269. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/multiplex.py +0 -0
  270. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/optimize.py +0 -0
  271. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/pipeline.py +0 -0
  272. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/report.py +0 -0
  273. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/router.py +0 -0
  274. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/seroprevalence_oracle.py +0 -0
  275. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/planner/target_site.py +0 -0
  276. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/__init__.py +0 -0
  277. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/corpus.py +0 -0
  278. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/embed.py +0 -0
  279. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/ground.py +0 -0
  280. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/index.py +0 -0
  281. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/llm.py +0 -0
  282. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/qa.py +0 -0
  283. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rag/retrieve.py +0 -0
  284. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rules/__init__.py +0 -0
  285. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rules/solver.py +0 -0
  286. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/rules/spec.py +0 -0
  287. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/__init__.py +0 -0
  288. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/audit.py +0 -0
  289. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/gate.py +0 -0
  290. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/policy.py +0 -0
  291. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/redteam.py +0 -0
  292. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/registry.py +0 -0
  293. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/screen.py +0 -0
  294. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/safety/standards.py +0 -0
  295. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/score/__init__.py +0 -0
  296. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/score/recalibrate.py +0 -0
  297. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/score/therapeutic.py +0 -0
  298. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/server/__init__.py +0 -0
  299. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/__init__.py +0 -0
  300. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/clarify.py +0 -0
  301. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/extract.py +0 -0
  302. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/resolvers/__init__.py +0 -0
  303. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/resolvers/cell.py +0 -0
  304. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/resolvers/chem.py +0 -0
  305. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/resolvers/feature.py +0 -0
  306. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/resolvers/gene.py +0 -0
  307. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/resolvers/locus.py +0 -0
  308. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/resolvers/phenotype.py +0 -0
  309. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/satisfy.py +0 -0
  310. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/service.py +0 -0
  311. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/spec/writespec.py +0 -0
  312. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/twin/__init__.py +0 -0
  313. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/twin/calibrate.py +0 -0
  314. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/twin/data/__init__.py +0 -0
  315. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/twin/data/position_effect.py +0 -0
  316. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/twin/mechanistic.py +0 -0
  317. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/twin/outcome.py +0 -0
  318. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/twin/position_effect.py +0 -0
  319. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/ui/__init__.py +0 -0
  320. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/ui/app.py +0 -0
  321. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/__init__.py +0 -0
  322. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/adapt_demo.py +0 -0
  323. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/agent_eval.py +0 -0
  324. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  325. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
  326. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/bench_graph_tasks.py +0 -0
  327. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/bench_rule_tasks.py +0 -0
  328. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/bench_trust_tasks.py +0 -0
  329. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  330. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  331. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/cargo_directionality.py +0 -0
  332. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/closed_loop.py +0 -0
  333. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/durability_baselines.py +0 -0
  334. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/experiment_design.py +0 -0
  335. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/expr_controls.py +0 -0
  336. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/forward_hypotheses.py +0 -0
  337. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/generative_design.py +0 -0
  338. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/guide_qc_demo.py +0 -0
  339. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/heldout_celltype_expr.py +0 -0
  340. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/immune_calibration.py +0 -0
  341. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/intent_specification.py +0 -0
  342. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/known_biology_expr.py +0 -0
  343. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  344. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  345. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/outcome_calibration.py +0 -0
  346. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/outcome_prediction.py +0 -0
  347. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/paper3_benchmark.py +0 -0
  348. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/paper4_real_validation.py +0 -0
  349. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/paper4_validation.py +0 -0
  350. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/protocol_safety.py +0 -0
  351. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/safety_screening.py +0 -0
  352. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/selective_prediction.py +0 -0
  353. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/seq_vs_measured.py +0 -0
  354. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/target_site_controls.py +0 -0
  355. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/uncertainty_eval.py +0 -0
  356. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/ungrounded_baseline.py +0 -0
  357. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/within_locus_ranking.py +0 -0
  358. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/validate/writer_recovery.py +0 -0
  359. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/verify/__init__.py +0 -0
  360. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/verify/schema.py +0 -0
  361. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/web/__init__.py +0 -0
  362. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/web/guide.py +0 -0
  363. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/web/llm.py +0 -0
  364. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/web/llm_provider.py +0 -0
  365. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/web/router.py +0 -0
  366. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/web/server.py +0 -0
  367. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/web/tools.py +0 -0
  368. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/__init__.py +0 -0
  369. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/chromatin_seq.py +0 -0
  370. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/durability.py +0 -0
  371. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/export_tracks.py +0 -0
  372. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/features.py +0 -0
  373. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/gsh_baseline.py +0 -0
  374. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/mesh_features.py +0 -0
  375. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/offtarget_assay.py +0 -0
  376. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/offtarget_data.py +0 -0
  377. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/offtarget_predict.py +0 -0
  378. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/ood.py +0 -0
  379. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/providers.py +0 -0
  380. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/safety.py +0 -0
  381. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/structure3d.py +0 -0
  382. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/uncertainty.py +0 -0
  383. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack/wgenome/writability.py +0 -0
  384. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack.egg-info/dependency_links.txt +0 -0
  385. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack.egg-info/entry_points.txt +0 -0
  386. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack.egg-info/requires.txt +0 -0
  387. {pen_stack-7.1.3 → pen_stack-7.1.5}/pen_stack.egg-info/top_level.txt +0 -0
  388. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_phase0.json +0 -0
  389. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  390. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_phase2.json +0 -0
  391. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_phase3.json +0 -0
  392. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_a.json +0 -0
  393. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_acq.json +0 -0
  394. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
  395. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
  396. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  397. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_b.json +0 -0
  398. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  399. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  400. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  401. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  402. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_c.json +0 -0
  403. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  404. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_calib.json +0 -0
  405. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
  406. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_chat.json +0 -0
  407. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_cite.json +0 -0
  408. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_closedloop.json +0 -0
  409. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_continual.json +0 -0
  410. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
  411. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_crit.json +0 -0
  412. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  413. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_d.json +0 -0
  414. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_delivery.json +0 -0
  415. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_drift.json +0 -0
  416. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_e.json +0 -0
  417. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_env.json +0 -0
  418. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  419. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
  420. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_expr2.json +0 -0
  421. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_f.json +0 -0
  422. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
  423. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_g.json +0 -0
  424. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_gen.json +0 -0
  425. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
  426. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  427. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_h.json +0 -0
  428. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
  429. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_immune.json +0 -0
  430. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_immune2.json +0 -0
  431. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
  432. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_innate.json +0 -0
  433. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_loop.json +0 -0
  434. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
  435. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  436. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
  437. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_mech.json +0 -0
  438. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  439. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_o.json +0 -0
  440. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_offtarget.json +0 -0
  441. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
  442. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_oracle.json +0 -0
  443. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_orch.json +0 -0
  444. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
  445. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
  446. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_peg.json +0 -0
  447. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_penchat.json +0 -0
  448. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_plan.json +0 -0
  449. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_policy.json +0 -0
  450. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_profile.json +0 -0
  451. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_proto.json +0 -0
  452. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_r.json +0 -0
  453. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
  454. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_route.json +0 -0
  455. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_screen.json +0 -0
  456. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
  457. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
  458. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
  459. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  460. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_v.json +0 -0
  461. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
  462. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_verify.json +0 -0
  463. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_writer.json +0 -0
  464. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_writespec.json +0 -0
  465. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  466. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/paper1.yaml +0 -0
  467. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/paper2.yaml +0 -0
  468. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/paper3.yaml +0 -0
  469. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/paper4.yaml +0 -0
  470. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/phase0.yaml +0 -0
  471. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_a.yaml +0 -0
  472. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_acq.yaml +0 -0
  473. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_aldesign.yaml +0 -0
  474. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_alvalidate.yaml +0 -0
  475. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_atlas.yaml +0 -0
  476. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_b.yaml +0 -0
  477. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_ba.yaml +0 -0
  478. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_ba_v33.yaml +0 -0
  479. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_ba_v45.yaml +0 -0
  480. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_bench.yaml +0 -0
  481. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_c.yaml +0 -0
  482. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_cal.yaml +0 -0
  483. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_calib.yaml +0 -0
  484. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_challenge.yaml +0 -0
  485. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_chat.yaml +0 -0
  486. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_cite.yaml +0 -0
  487. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_closedloop.yaml +0 -0
  488. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_continual.yaml +0 -0
  489. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_cosci2.yaml +0 -0
  490. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_crit.yaml +0 -0
  491. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_ct.yaml +0 -0
  492. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_d.yaml +0 -0
  493. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_delivery.yaml +0 -0
  494. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_drift.yaml +0 -0
  495. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_e.yaml +0 -0
  496. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_env.yaml +0 -0
  497. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_ep.yaml +0 -0
  498. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_epitope.yaml +0 -0
  499. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_expr2.yaml +0 -0
  500. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_f.yaml +0 -0
  501. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_frontend.yaml +0 -0
  502. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_g.yaml +0 -0
  503. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_gen.yaml +0 -0
  504. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_genotox.yaml +0 -0
  505. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_graph.yaml +0 -0
  506. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_h.yaml +0 -0
  507. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_hybrid.yaml +0 -0
  508. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_immune.yaml +0 -0
  509. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_immune2.yaml +0 -0
  510. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_ingest.yaml +0 -0
  511. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_innate.yaml +0 -0
  512. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_loop.yaml +0 -0
  513. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_manifest.yaml +0 -0
  514. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_mc.yaml +0 -0
  515. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_mcp.yaml +0 -0
  516. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_mech.yaml +0 -0
  517. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_mon.yaml +0 -0
  518. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_o.yaml +0 -0
  519. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_offtarget.yaml +0 -0
  520. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_openapi.yaml +0 -0
  521. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_oracle.yaml +0 -0
  522. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_orch.yaml +0 -0
  523. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_outcome.yaml +0 -0
  524. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_pareto.yaml +0 -0
  525. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_peg.yaml +0 -0
  526. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_penchat.yaml +0 -0
  527. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_plan.yaml +0 -0
  528. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_policy.yaml +0 -0
  529. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_profile.yaml +0 -0
  530. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_proto.yaml +0 -0
  531. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_r.yaml +0 -0
  532. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_redteam.yaml +0 -0
  533. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_route.yaml +0 -0
  534. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_screen.yaml +0 -0
  535. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_seroprev.yaml +0 -0
  536. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_simlab.yaml +0 -0
  537. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_twincal.yaml +0 -0
  538. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_uq.yaml +0 -0
  539. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_v.yaml +0 -0
  540. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_vcell.yaml +0 -0
  541. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_verify.yaml +0 -0
  542. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_writer.yaml +0 -0
  543. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_writespec.yaml +0 -0
  544. {pen_stack-7.1.3 → pen_stack-7.1.5}/prereg/ws_wv.yaml +0 -0
  545. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/build_capsid_fitness.py +0 -0
  546. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/build_rag_corpus.py +0 -0
  547. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/calibrate_immune_axes.py +0 -0
  548. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/fetch_licensed_sources.py +0 -0
  549. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/offtarget_chromatin_incremental.py +0 -0
  550. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/offtarget_chromatin_matched.py +0 -0
  551. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/offtarget_chromatin_validation.py +0 -0
  552. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_build_atlas.py +0 -0
  553. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_build_durability.py +0 -0
  554. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_build_position_effect.py +0 -0
  555. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_build_writer_eff.py +0 -0
  556. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_export_tracks.py +0 -0
  557. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_safety_concordance.py +0 -0
  558. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_train_safety.py +0 -0
  559. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p1_validation_report.py +0 -0
  560. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p2_build_atlas.py +0 -0
  561. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p3_benchmark_report.py +0 -0
  562. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p4_genome_scan.py +0 -0
  563. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p52_build_genotox_oracle.py +0 -0
  564. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/p53_build_epitope_oracle.py +0 -0
  565. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/ws_b_report.py +0 -0
  566. {pen_stack-7.1.3 → pen_stack-7.1.5}/scripts/ws_c_report.py +0 -0
  567. {pen_stack-7.1.3 → pen_stack-7.1.5}/setup.cfg +0 -0
@@ -3,6 +3,58 @@
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.1.5] - 2026-06-29 - Chromosome field: validation, gene concordance, context
7
+
8
+ ### Added
9
+ - The `chrom` design field was a free-text pass-through with no validation and no downstream effect. It is now
10
+ meaningful (`pen_stack/planner/chromosome.py`):
11
+ - **Validation** — the web form's Chromosome input is a controlled dropdown (chr1–chr22, chrX, chrY, chrM), so
12
+ an impossible value like `chrZZZ` can no longer be entered; `canonical_chromosome()` normalises/validates on
13
+ the backend (`chrX`/`X`/`x`, `chr1`/`1`, `chrM`/`chrMT`/`MT`, `23`→X, `24`→Y; rejects the rest).
14
+ - **Gene/chromosome concordance** — `verify()` now flags `chromosome_mismatch` when the entered chromosome does
15
+ not match the named gene's canonical location (e.g. *BRCA1 is on chr17, not chr1*), `chromosome_invalid` for a
16
+ non-standard value, and `chromosome_unverifiable` for an unknown gene. The web form shows the warning inline
17
+ with a one-click fix, driven by the new `GET /api/gene/location` endpoint. This closes the silent-mismatch gap.
18
+ - **Chromosome context** — `chromosome_context()` adds grounded, chromosome-driven advisories surfaced as scope
19
+ flags: **chrM** is not addressable by nuclear genome-writing tools (mtDNA needs DdCBE/TALED — out of scope),
20
+ **chrY** is male-specific with ampliconic repeats, **chrX** is hemizygous in 46,XY vs X-inactivated in 46,XX.
21
+ - The Design Studio **cell-type dropdown now shows writability-atlas coverage** (from `/api/celltypes`): K562 /
22
+ HepG2 / HSPC are labelled measured (full / full / partial), while h1_hesc / iPSC / CD8 T / PBMC are labelled
23
+ "no atlas" — so a data-gated cell type is no longer silently indistinguishable from a measured one (the same
24
+ honest-field principle as the chromosome field). The 7 cell types are unchanged and already matched the backend.
25
+
26
+ ### Note (honest scoping)
27
+ - The free-text chromosome does **not** move the scored locus: per-locus safety / durability / accessibility /
28
+ off-target are indexed by the gene's *resolved genomic coordinates* via the writability atlas, so a bare
29
+ chromosome string with no position cannot refine them. Rather than fabricate a per-chromosome score delta, the
30
+ field's grounded effect is validation + gene concordance (ensuring the score is read for the right locus, and
31
+ flagging when the entered chromosome disagrees with the gene) + the chromosome-context advisories above.
32
+
33
+ ## [7.1.4] - 2026-06-29 - Germline-prohibition legality rule
34
+
35
+ ### Added
36
+ - **Compliance rule category + `compliance.germline_prohibition` (hard_reject).** A heritable / germline edit
37
+ previously passed the Legality axis because no rule existed for it (the only germline handling was a
38
+ known-unknown scope matcher for *questions*, not *designs*). The new rule fails legality for: a reproductive
39
+ germline target cell (embryo / zygote / oocyte / sperm / gamete - unconditional), a declared reproductive-use
40
+ intent (gametes / assisted reproduction / implantation / "for reproduction"), a declared heritable intent
41
+ (germline / heritable / "transmitted to offspring"), or a germline-competent cell type (hESC / iPSC) edited in a
42
+ heritable context (in vivo, or a reproductive/heritable intent). It is cited (Lander et al. 2019 moratorium,
43
+ `10.1038/d41586-019-00726-5`; US National Academies 2017, `10.17226/24623`) and emits a proof-object repair hint
44
+ (restrict to somatic / ex-vivo editing). It is a scope-of-use legality bound, reported on the LEGALITY axis,
45
+ separate from the Biosecurity axis. The category is wired into every write type; `rule_spec.json` is now 11 rules.
46
+ - **Paraphrase + negation robustness** (red-team-hardened). The free-text intent match is NEGATION-aware
47
+ (negated-span detection), so a somatic design that explicitly says "somatic, not germline" / "no germline
48
+ transmission" / "NOT for reproduction or implantation" is not false-flagged, while paraphrased reproductive
49
+ intent (e.g. "generate gametes for assisted reproduction", "embryo intended for uterine transfer") with no
50
+ literal "germline"/"heritable" word still fails. Validated against a 30-design adversarial set: 0 evasions,
51
+ 0 false positives.
52
+
53
+ ### Fixed
54
+ - The Verify page's Legality description claimed it adjudicates "jurisdiction / list-agent / IP", which it does
55
+ not. Corrected to describe the actual legality axis (physical feasibility + germline scope-of-use compliance),
56
+ noting that dual-use hazard is the separate Biosecurity axis.
57
+
6
58
  ## [7.1.3] - 2026-06-26 - Designer correctness: Guardian biosecurity + calibrated confidence
7
59
 
8
60
  ### Fixed
@@ -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.3
5
- date-released: 2026-06-26
4
+ version: 7.1.5
5
+ date-released: 2026-06-29
6
6
  authors:
7
7
  - family-names: "Mahaboob Ali"
8
8
  given-names: "Anees Ahmed"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 7.1.3
3
+ Version: 7.1.5
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,44 @@
1
+ # Compliance / scope-of-use legality (v7.1.4). PEN-STACK is a SOMATIC genome-writing design tool: it does not
2
+ # model germline transmission, embryonic development, or heritable outcomes, and it does not support designing a
3
+ # heritable human germline edit. A design that declares a heritable / germline-editing intent, OR that targets a
4
+ # reproductive germline cell, OR that edits a germline-competent cell type in vivo, is a HARD legality reject -
5
+ # heritable human germline genome editing is broadly prohibited / under international moratorium (it is NOT a
6
+ # biosecurity-hazard signature, so it must fail on the LEGALITY axis, leaving biosecurity to its own screen).
7
+ category: compliance
8
+ rules:
9
+ - id: compliance.germline_prohibition
10
+ kind: hard_reject
11
+ category: compliance
12
+ mechanism: "heritable human germline editing is out of scope for this somatic tool and broadly prohibited: a declared germline/heritable intent, a reproductive germline target cell, or an in-vivo edit of a germline-competent cell type is rejected"
13
+ evaluator: germline_prohibition
14
+ param:
15
+ # reproductive germline cells: editing these IS germline (always reject, any context; matched on the
16
+ # structured cell_type field, so no negation handling needed).
17
+ germline_cell_types: ["embryo", "zygote", "oocyte", "ovum", "egg cell", "sperm", "spermatozoa",
18
+ "spermatocyte", "spermatogonia", "spermatogonial", "gamete", "germ cell", "germline",
19
+ "primordial germ", "pgc", "blastocyst", "conceptus", "morula", "preimplantation"]
20
+ # germline-competent pluripotent cells: editing these in a HERITABLE context (in vivo, or a reproductive /
21
+ # heritable intent) is a germ-lineage edit; an ex-vivo SOMATIC use of the same research line is in scope.
22
+ germline_competent_cell_types: ["h1_hesc", "hesc", "embryonic stem", "ipsc", "ips cell", "ipscs",
23
+ "pluripotent stem", "epiblast", "inner cell mass"]
24
+ # STRONG reproductive-use intent: describes a reproductive PURPOSE; matched negation-aware in the free text.
25
+ reproductive_intent_terms: ["gamete", "gametes", "generate gametes", "derive gametes", "primordial germ",
26
+ "germ lineage", "germ-line", "germ line", "for reproduction", "reproductive use",
27
+ "reproductive purpose", "assisted reproduction", "ivf", "in vitro fertilization",
28
+ "implantation", "uterine transfer", "fertilized egg", "for pregnancy",
29
+ "destined for transfer", "slated for transfer", "scheduled for implantation",
30
+ "intended for pregnancy", "reproductive editing"]
31
+ # WEAKER heritable-transmission intent: can also appear inside a negated somatic disclaimer, so matched
32
+ # negation-aware (a design that says "not heritable" / "no germline transmission" does not fire on these).
33
+ heritable_terms: ["germline", "heritable", "inheritable", "inherited by", "inherit the", "heritably",
34
+ "pass to offspring", "passed to offspring", "passed down to", "transmit to offspring",
35
+ "transmit the fix to", "transmissible to progeny", "transmitted to the", "to progeny",
36
+ "to descendants", "to the patient's children", "to the man's offspring",
37
+ "future generations", "future generation", "next generation edit", "germ cell editing",
38
+ "embryo editing", "embryonic editing", "carried by the resulting child",
39
+ "inheritable by future"]
40
+ provenance:
41
+ doi: ["10.1038/d41586-019-00726-5", "10.17226/24623"]
42
+ note: "Heritable human germline genome editing is broadly prohibited / under moratorium: Lander et al. 2019 international call for a moratorium (Nature 567:165); US National Academies 2017 'Human Genome Editing: Science, Ethics, and Governance'; WHO Expert Advisory Committee framework (2021); US FDA is barred from considering heritable-germline clinical applications (Consolidated Appropriations Act rider); Council of Europe Oviedo Convention Art. 13. PEN-STACK is a SOMATIC tool and takes no position on germline editing beyond declaring it out of scope."
43
+ test_ref: "tests/unit/test_ws_r.py::test_germline_edit_fails_legality"
44
+ scope: "a scope-of-use / regulatory legality bound, not a biosecurity-hazard signature and not an efficacy claim"
@@ -9,35 +9,35 @@ write_types:
9
9
  insertion:
10
10
  status: supported
11
11
  description: "insert a cargo at a safe/durable locus"
12
- rule_categories: [reachability, fold, payload, delivery]
12
+ rule_categories: [reachability, fold, payload, delivery, compliance]
13
13
  steps: [site_selection, writer_selection, cargo_design, offtarget, structural_risk, verify]
14
14
  writer_classes: [bridge, cast, serine_integrase, pe_integrase]
15
15
 
16
16
  excision:
17
17
  status: coverage_only
18
18
  description: "excise a segment (e.g. BCL11A enhancer, FXN GAA repeat) via a recombinase/nuclease pair"
19
- rule_categories: [reachability, multiplex, delivery]
19
+ rule_categories: [reachability, multiplex, delivery, compliance]
20
20
  steps: [breakpoint_reachability, pair_safety, delivery, verify]
21
21
  writer_classes: [serine_integrase, recombinase, nuclease_pair]
22
22
 
23
23
  inversion:
24
24
  status: coverage_only
25
25
  description: "invert a segment between two recombination sites"
26
- rule_categories: [reachability, multiplex, delivery]
26
+ rule_categories: [reachability, multiplex, delivery, compliance]
27
27
  steps: [site_pair_reachability, orientation_check, delivery, verify]
28
28
  writer_classes: [serine_integrase, recombinase]
29
29
 
30
30
  replacement:
31
31
  status: coverage_only
32
32
  description: "replace a segment (excise + insert)"
33
- rule_categories: [reachability, fold, payload, multiplex, delivery]
33
+ rule_categories: [reachability, fold, payload, multiplex, delivery, compliance]
34
34
  steps: [excision_reachability, insertion_reachability, payload, delivery, verify]
35
35
  writer_classes: [pe_integrase, serine_integrase, cast]
36
36
 
37
37
  regulatory_rewrite:
38
38
  status: coverage_only
39
39
  description: "edit a regulatory element (enhancer/silencer); routes to AlphaGenome variant-effect + scope"
40
- rule_categories: [reachability, delivery]
40
+ rule_categories: [reachability, delivery, compliance]
41
41
  steps: [variant_effect_oracle, regulatory_scope, delivery, verify]
42
42
  writer_classes: [base_editor, pe, nuclease]
43
43
  scope_note: "regulatory-element function change is partly out of scope (structure->phenotype); flagged"
@@ -45,13 +45,13 @@ write_types:
45
45
  landing_pad_install:
46
46
  status: coverage_only
47
47
  description: "install an att landing pad (PE), then a deterministic integrase step"
48
- rule_categories: [reachability, payload, delivery]
48
+ rule_categories: [reachability, payload, delivery, compliance]
49
49
  steps: [pe_installability, integrase_step, payload, delivery, verify]
50
50
  writer_classes: [pe_integrase]
51
51
 
52
52
  multiplex:
53
53
  status: supported
54
54
  description: "multiple concurrent edits; routes to the translocation screen"
55
- rule_categories: [reachability, multiplex, delivery]
55
+ rule_categories: [reachability, multiplex, delivery, compliance]
56
56
  steps: [per_edit_reachability, translocation_screen, delivery, verify]
57
57
  writer_classes: [any]
@@ -1,6 +1,6 @@
1
1
  # Genome-writing rule spec
2
2
 
3
- Version 1.0. 10 rules across 5 categories (delivery, fold, multiplex, payload, reachability). A rule is data, not code: each record names the evaluator that executes it against a Design, so the rules are enumerable, queryable, and citation-backed without changing any decision (proven by the parity tests).
3
+ Version 1.0. 11 rules across 6 categories (compliance, delivery, fold, multiplex, payload, reachability). A rule is data, not code: each record names the evaluator that executes it against a Design, so the rules are enumerable, queryable, and citation-backed without changing any decision (proven by the parity tests).
4
4
 
5
5
  Generated from `pen_stack/rules/spec.py::export_spec`. Machine-readable form: `benchmarks/verify/rule_spec.json`.
6
6
 
@@ -16,3 +16,4 @@ Generated from `pen_stack/rules/spec.py::export_spec`. Machine-readable form: `b
16
16
  | `delivery.sequence_constraints` | soft_penalty | vehicle-specific construct hazards (lentiviral internal poly(A), AAV inverted-repeat/homopolymer, recombinogenic direct repeats, GC extremes) lower titre/yield | 10.1128/JVI.79.15.9933-9944.2005 | labeled heuristic, directional; not a titre predictor |
17
17
  | `delivery.aav_packaging_margin` | soft_penalty | AAV packaging efficiency / titre drops sharply as the cargo approaches the capsid limit (computable from cargo_bp vs vehicle capacity), even while still under capacity (v4.0 delivery-oracle refinement) | 10.1089/hum.2010.245 | computable efficiency margin, directional; not a titre predictor |
18
18
  | `delivery.immunogenicity_magnitude` | scope_flag | the MAGNITUDE of immunogenicity and the precise in-vivo tropism of a vehicle are not modeled | known-unknown; surfaced, never predicted (Invariant 2) | declared out of scope; never a hard reject dressed as physics |
19
+ | `compliance.germline_prohibition` | hard_reject | heritable human germline editing is out of scope for this somatic tool and broadly prohibited: a declared germline/heritable intent, a reproductive germline target cell, or an in-vivo edit of a germline-competent cell type is rejected (somatic/ex-vivo use of a germline-competent research line is in scope) | 10.1038/d41586-019-00726-5, 10.17226/24623 | a scope-of-use / regulatory legality bound, not a biosecurity-hazard signature and not an efficacy claim |
@@ -1,2 +1,2 @@
1
1
  """PEN-STACK v3.0 - open infrastructure for genome writing."""
2
- __version__ = "7.1.3"
2
+ __version__ = "7.1.5"
@@ -0,0 +1,106 @@
1
+ """Chromosome validation, gene/chromosome concordance, and chromosome-context advisories (v7.1.5).
2
+
3
+ The `chrom` field on a design was previously a free-text pass-through with no validation and no downstream
4
+ effect. This module makes it meaningful WITHOUT fabricating a score: the engine's per-locus safety / durability /
5
+ accessibility / off-target numbers are indexed by the GENE's resolved genomic coordinates via the writability
6
+ atlas (`planner.optimize.gene_region`), not by a free-text chromosome string - a bare "chrX" with no position
7
+ cannot index a specific locus value. So the honest, grounded use of the field is:
8
+
9
+ * `canonical_chromosome` - validate / normalise to a standard human chromosome (chr1-chr22, chrX, chrY, chrM);
10
+ * `gene_chromosome` - the gene's canonical chromosome from the gene-coordinate table;
11
+ * `chromosome_concordance` - flag when the entered chromosome does not match the named gene's real location
12
+ (the "BRCA1 is on chr17, not chr1" case), making explicit that scoring uses the gene's canonical locus;
13
+ * `chromosome_context` - chromosome-driven advisories that ARE grounded in biology: chrM is not addressable by
14
+ nuclear genome-writing tools (mtDNA needs DdCBE/TALED - out of scope), chrY is male-specific with ampliconic
15
+ repeats, chrX is hemizygous in 46,XY vs X-inactivated in 46,XX.
16
+ """
17
+ from __future__ import annotations
18
+
19
+ import re
20
+
21
+ STANDARD_CHROMOSOMES = [f"chr{i}" for i in range(1, 23)] + ["chrX", "chrY", "chrM"]
22
+
23
+
24
+ def canonical_chromosome(chrom: str | None) -> str | None:
25
+ """Normalise a chromosome string to canonical UCSC form (chr1..chr22, chrX, chrY, chrM), or None if it is not
26
+ a standard human chromosome. Accepts 'chrX'/'X'/'x', 'chr1'/'1', 'chrM'/'chrMT'/'MT', '23'->X, '24'->Y."""
27
+ if chrom is None:
28
+ return None
29
+ c = re.sub(r"\s+", "", str(chrom)).lower()
30
+ if not c:
31
+ return None
32
+ if c.startswith("chr"):
33
+ c = c[3:]
34
+ c = {"mt": "m", "23": "x", "24": "y", "25": "m"}.get(c, c)
35
+ if c in {"x", "y", "m"}:
36
+ return "chr" + c.upper()
37
+ if c.isdigit() and 1 <= int(c) <= 22:
38
+ return "chr" + c
39
+ return None
40
+
41
+
42
+ def gene_chromosome(gene: str | None) -> str | None:
43
+ """The canonical chromosome of a gene (or safe-harbour locus nickname), from the gene-coordinate table; None
44
+ if the gene is unknown."""
45
+ if not gene:
46
+ return None
47
+ try:
48
+ from pen_stack.planner.optimize import gene_region
49
+ reg = gene_region(gene)
50
+ except Exception: # noqa: BLE001 - coords table absent (bare checkout) -> cannot resolve, do not fabricate
51
+ return None
52
+ return canonical_chromosome(reg[0]) if reg else None
53
+
54
+
55
+ def chromosome_concordance(gene: str | None, chrom: str | None) -> dict:
56
+ """Validate the entered chromosome and check it against the named gene's canonical location.
57
+
58
+ status is one of:
59
+ * 'invalid' - the entered string is not a standard human chromosome;
60
+ * 'mismatch' - valid, but does not match the gene's canonical chromosome (the silent-mismatch case);
61
+ * 'match' - matches the gene's canonical chromosome;
62
+ * 'unverifiable' - valid chromosome but the gene's location is unknown (cannot confirm);
63
+ * 'none' - no chromosome supplied.
64
+ """
65
+ entered = canonical_chromosome(chrom)
66
+ gchrom = gene_chromosome(gene)
67
+ out = {"entered_raw": chrom, "entered": entered, "valid": entered is not None,
68
+ "gene": gene, "gene_chrom": gchrom}
69
+ if chrom in (None, "") or str(chrom).strip() == "":
70
+ out["status"] = "none"
71
+ out["message"] = None
72
+ elif entered is None:
73
+ out["status"] = "invalid"
74
+ out["message"] = (f"'{chrom}' is not a standard human chromosome. Use chr1-chr22, chrX, chrY, or chrM.")
75
+ elif gchrom and entered != gchrom:
76
+ out["status"] = "mismatch"
77
+ out["message"] = (f"{gene} is on {gchrom}, not {entered}. Locus scoring uses {gene}'s canonical location "
78
+ f"({gchrom}); the entered chromosome does not move the locus.")
79
+ elif gchrom and entered == gchrom:
80
+ out["status"] = "match"
81
+ out["message"] = f"{entered} matches the canonical location of {gene}."
82
+ else:
83
+ out["status"] = "unverifiable"
84
+ out["message"] = (f"{entered} is a valid chromosome, but {gene or 'the gene'} is not in the coordinate "
85
+ "table, so the gene/chromosome match cannot be confirmed.")
86
+ return out
87
+
88
+
89
+ def chromosome_context(chrom: str | None) -> dict | None:
90
+ """A grounded, chromosome-driven advisory (or None for an autosome). These are real biological constraints
91
+ the chromosome identity alone implies; magnitudes remain locus/subject-dependent (not predicted here)."""
92
+ c = canonical_chromosome(chrom)
93
+ if c == "chrM":
94
+ return {"chrom": "chrM", "severity": "high",
95
+ "note": ("the mitochondrial genome is NOT addressable by nuclear genome-writing tools "
96
+ "(integrases / recombinases / nuclear-targeted Cas9); mtDNA editing requires "
97
+ "mitochondrially-targeted base editors (DdCBE / TALED) - out of scope for this tool.")}
98
+ if c == "chrY":
99
+ return {"chrom": "chrY", "severity": "medium",
100
+ "note": ("the Y chromosome is male-specific (absent in 46,XX) and rich in ampliconic / palindromic "
101
+ "repeats, so confirm the target is single-copy before trusting a unique-site assumption.")}
102
+ if c == "chrX":
103
+ return {"chrom": "chrX", "severity": "low",
104
+ "note": ("the X chromosome is hemizygous in 46,XY (one copy) but subject to X-inactivation in "
105
+ "46,XX (mosaic expression); dosage and durability are sex- and locus-dependent.")}
106
+ return None
@@ -11,6 +11,7 @@ YAML record + (if new) one evaluator here, never scattered ``if`` checks in the
11
11
  """
12
12
  from __future__ import annotations
13
13
 
14
+ import re
14
15
  from collections.abc import Callable
15
16
 
16
17
  from pen_stack.rules.schema import Design, Rule, RuleResult
@@ -204,6 +205,95 @@ def delivery_immunogenicity_scope(design: Design, rule: Rule) -> RuleResult:
204
205
  "(declared out of scope, never predicted)")
205
206
 
206
207
 
208
+ # --------------------------------------------------------------------------------------------------
209
+ # compliance / scope-of-use legality (v7.1.4): heritable human germline editing is out of scope + prohibited
210
+ # --------------------------------------------------------------------------------------------------
211
+ # A term in declared free text counts only if it is NOT inside a NEGATED span, so a somatic design that says
212
+ # "no germline transmission", "not heritable", "somatic, not germline", or "NOT for reproduction or implantation"
213
+ # (a distributed negation over a list) does not false-fire on the negated word. A negator opens a span that runs
214
+ # across list connectors (or / and / ,) until a clause break (. ; but however yet) or ~60 chars.
215
+ _NEGATOR = re.compile(r"\b(not|non|no|never|without|isn'?t|aren'?t|rather than|as opposed to)\b")
216
+ _CLAUSE_BREAK = re.compile(r"[;.]|\bbut\b|\bhowever\b|\byet\b|\bexcept\b")
217
+
218
+
219
+ def _negated_spans(text: str) -> list[tuple[int, int]]:
220
+ spans = []
221
+ for m in _NEGATOR.finditer(text):
222
+ rest = text[m.end():m.end() + 60]
223
+ brk = _CLAUSE_BREAK.search(rest)
224
+ spans.append((m.start(), m.end() + (brk.start() if brk else len(rest))))
225
+ return spans
226
+
227
+
228
+ def _intent_present(term: str, text: str, spans: list[tuple[int, int]]) -> bool:
229
+ t = str(term or "").strip().lower()
230
+ if not t:
231
+ return False
232
+ idx = text.find(t)
233
+ while idx != -1:
234
+ if not any(s <= idx < e for s, e in spans):
235
+ return True # a non-negated occurrence
236
+ idx = text.find(t, idx + 1)
237
+ return False
238
+
239
+
240
+ @evaluator("germline_prohibition")
241
+ def germline_prohibition(design: Design, rule: Rule) -> RuleResult:
242
+ """Hard legality reject for a heritable / germline edit. Fires on (a) a reproductive germline TARGET cell
243
+ (embryo/zygote/oocyte/sperm/gamete - structured, unconditional), (b) a declared REPRODUCTIVE-USE intent
244
+ (gametes / assisted reproduction / implantation / 'for reproduction'), (c) a declared HERITABLE intent
245
+ (germline / heritable / transmitted to offspring), or (d) a germline-COMPETENT cell type (hESC/iPSC) in a
246
+ heritable context (in vivo, or a reproductive/heritable intent). The free-text intent matches are NEGATION-
247
+ aware, so a SOMATIC design that explicitly says 'somatic, not germline' / 'no germline transmission' / 'not
248
+ heritable' is NOT false-flagged, and an ex-vivo somatic use of a germline-competent research line stays in
249
+ scope. The screen reads the declared design artifact only (not any free-text justification)."""
250
+ extra = design.model_extra or {}
251
+
252
+ def _norm(x) -> str:
253
+ return str(x or "").strip().lower()
254
+
255
+ text = " ".join(_norm(x) for x in (design.edit_intent, extra.get("cargo_function"),
256
+ extra.get("goal_function"), extra.get("function_annotation"),
257
+ extra.get("notes")) if x)
258
+ ct = _norm(design.cell_type)
259
+ in_vivo = bool(extra.get("in_vivo")) and _norm(extra.get("in_vivo")) not in ("false", "0", "no")
260
+ p = rule.param
261
+
262
+ spans = _negated_spans(text)
263
+ repro_ct = [c for c in p.get("germline_cell_types", []) if _norm(c) in ct]
264
+ competent_ct = [c for c in p.get("germline_competent_cell_types", []) if _norm(c) in ct]
265
+ repro_intent = [t for t in p.get("reproductive_intent_terms", []) if _intent_present(_norm(t), text, spans)]
266
+ heritable = [t for t in p.get("heritable_terms", []) if _intent_present(_norm(t), text, spans)]
267
+
268
+ triggers = []
269
+ if repro_ct:
270
+ triggers.append(f"a reproductive germline target cell ('{repro_ct[0]}')")
271
+ if repro_intent:
272
+ triggers.append(f"a declared reproductive-use intent ('{repro_intent[0]}')")
273
+ if heritable:
274
+ triggers.append(f"a declared heritable/germline-editing intent ('{heritable[0]}')")
275
+ if competent_ct and (in_vivo or repro_intent or heritable):
276
+ ctx = "in vivo" if in_vivo else (repro_intent or heritable)[0]
277
+ triggers.append(f"a germline-competent cell type ('{competent_ct[0]}') edited in a heritable context "
278
+ f"('{ctx}')")
279
+ if triggers:
280
+ # de-duplicate while preserving order, then report
281
+ seen, uniq = set(), []
282
+ for t in triggers:
283
+ if t not in seen:
284
+ seen.add(t)
285
+ uniq.append(t)
286
+ return _result(rule, "violate",
287
+ "heritable human germline editing is out of scope for this somatic tool and is broadly "
288
+ "prohibited (international moratorium): " + "; ".join(uniq)
289
+ + ". Restrict to somatic editing (ex vivo, or a somatic cell type / somatic context).")
290
+ note = ""
291
+ if competent_ct:
292
+ note = (f" (cell type '{competent_ct[0]}' is germline-competent; somatic/ex-vivo editing of it is in "
293
+ "scope, but an in-vivo or reproductive heritable edit would not be)")
294
+ return _result(rule, "pass", "no heritable/germline-editing intent or germline target declared" + note)
295
+
296
+
207
297
  # --------------------------------------------------------------------------------------------------
208
298
  # multiplex translocation (delegates to planner.multiplex, soft penalty)
209
299
  # --------------------------------------------------------------------------------------------------
@@ -10,7 +10,7 @@ from pen_stack._resources import resource
10
10
  from pen_stack.rules.schema import Rule, Ruleset
11
11
 
12
12
  RULES_VERSION = "1.0"
13
- _CATEGORIES = ("reachability", "fold", "payload", "multiplex", "delivery")
13
+ _CATEGORIES = ("reachability", "fold", "payload", "multiplex", "delivery", "compliance")
14
14
 
15
15
 
16
16
  @lru_cache(maxsize=1)
@@ -18,7 +18,7 @@ from pydantic import BaseModel, Field
18
18
 
19
19
  RuleKind = Literal["hard_reject", "soft_penalty", "scope_flag"]
20
20
  RuleStatus = Literal["pass", "violate", "flag", "scope", "not_applicable"]
21
- RuleCategory = Literal["reachability", "fold", "payload", "multiplex", "delivery"]
21
+ RuleCategory = Literal["reachability", "fold", "payload", "multiplex", "delivery", "compliance"]
22
22
 
23
23
 
24
24
  class Rule(BaseModel):
@@ -119,6 +119,23 @@ def crosslink_loci(request: Request, family: str, ct: str = "k562", cell_type: s
119
119
  return {"family": family, "ct": ct, "loci": _records(loci), "disclaimer": _DISCLAIMER}
120
120
 
121
121
 
122
+ @app.get("/gene/location")
123
+ def gene_location(gene: str):
124
+ """v7.1.5: the canonical chromosome of a gene (or safe-harbour locus nickname), for the UI's gene/chromosome
125
+ concordance check. found=False when the gene is not in the coordinate table (no fabrication)."""
126
+ from pen_stack.planner.chromosome import canonical_chromosome
127
+ from pen_stack.planner.optimize import gene_region, resolve_gene
128
+ try:
129
+ reg = gene_region(gene)
130
+ except Exception: # noqa: BLE001 - coords table absent -> cannot resolve
131
+ reg = None
132
+ resolved = resolve_gene(gene)
133
+ if reg is None:
134
+ return {"gene": gene, "resolved": resolved, "found": False, "chrom": None}
135
+ return {"gene": gene, "resolved": resolved, "found": True, "chrom": canonical_chromosome(reg[0]),
136
+ "start": int(reg[1]), "end": int(reg[2])}
137
+
138
+
122
139
  @app.get("/writable")
123
140
  def writable(request: Request, gene: str, ct: str = "k562", cell_type: str | None = None,
124
141
  top: int = Query(20, le=200)):
@@ -88,6 +88,13 @@ def _repair_for_violation(d: Design, rule_id: str) -> dict[str, Any] | None:
88
88
  return {"text": "the writer cannot reach the target as specified; declare a pre-installed landing pad "
89
89
  "(installed_att) or choose a writer family whose target element is present.",
90
90
  "repair": {"field": "installed_att", "set_to": True}}
91
+ if rule_id == "compliance.germline_prohibition":
92
+ return {"text": "this is a heritable / germline edit, which is out of scope for this somatic-editing "
93
+ "tool and is broadly prohibited (international moratorium); restrict to SOMATIC editing - "
94
+ "move to ex vivo (in_vivo = false) and/or a somatic cell type (e.g. hepatocytes / HepG2, "
95
+ "HSPCs), and drop any germline/heritable intent. (Not auto-repairable: a germline edit is "
96
+ "a change of intent, not a field fix.)",
97
+ "repair": None}
91
98
  return None
92
99
 
93
100
 
@@ -98,6 +98,18 @@ def verify(design: Design | dict, question: str | None = None, *, actor: str = "
98
98
  for r in results if r["kind"] == "scope_flag" and r["status"] == "scope"]
99
99
  scope_flags += rule_scope
100
100
 
101
+ # v7.1.5: chromosome validation + gene/chromosome concordance + chromosome-context advisory. The free-text
102
+ # chrom field does not move the scored locus (scoring is indexed by the gene's resolved coordinates), so a
103
+ # mismatch / invalid value / chromosome-context note is surfaced as a scope flag, never silently ignored.
104
+ from pen_stack.planner.chromosome import chromosome_concordance, chromosome_context
105
+ conc = chromosome_concordance(design.gene, getattr(design, "chrom", None))
106
+ if conc["status"] in ("invalid", "mismatch", "unverifiable"):
107
+ scope_flags.append({"kind": f"chromosome_{conc['status']}", "reason": conc["message"],
108
+ "entered": conc["entered"], "gene_chrom": conc["gene_chrom"]})
109
+ ctx = chromosome_context(getattr(design, "chrom", None))
110
+ if ctx:
111
+ scope_flags.append({"kind": "chromosome_context", "chrom": ctx["chrom"], "reason": ctx["note"]})
112
+
101
113
  pc = _plan_confidence(design)
102
114
  verdict = classify(grounded=True, confidence=pc["confidence"], out_of_scope=oos_hit)
103
115
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 7.1.3
3
+ Version: 7.1.5
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
@@ -63,6 +63,7 @@ configs/expression/promoters.yaml
63
63
  configs/oracles/execution.yaml
64
64
  configs/oracles/reliability.yaml
65
65
  configs/oracles/scope_cards.yaml
66
+ configs/rules/compliance.yaml
66
67
  configs/rules/delivery.yaml
67
68
  configs/rules/fold.yaml
68
69
  configs/rules/multiplex.yaml
@@ -251,6 +252,7 @@ pen_stack/planner/antipeg_oracle.py
251
252
  pen_stack/planner/capsid_epitope_oracle.py
252
253
  pen_stack/planner/cargo.py
253
254
  pen_stack/planner/cargo_polish.py
255
+ pen_stack/planner/chromosome.py
254
256
  pen_stack/planner/delivery.py
255
257
  pen_stack/planner/delivery_constraints.py
256
258
  pen_stack/planner/delivery_immune.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pen-stack"
7
- version = "7.1.3"
7
+ version = "7.1.5"
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"
File without changes
File without changes
File without changes
File without changes