pen-stack 5.8.0__tar.gz → 5.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. {pen_stack-5.8.0 → pen_stack-5.9.0}/CHANGELOG.md +36 -0
  2. {pen_stack-5.8.0 → pen_stack-5.9.0}/CITATION.cff +1 -1
  3. {pen_stack-5.8.0 → pen_stack-5.9.0}/PKG-INFO +30 -9
  4. {pen_stack-5.8.0 → pen_stack-5.9.0}/README.md +29 -8
  5. {pen_stack-5.8.0 → pen_stack-5.9.0}/benchmarks/genome_writing_bench/SHA256SUMS +1 -1
  6. {pen_stack-5.8.0 → pen_stack-5.9.0}/benchmarks/genome_writing_bench/tasks.yaml +20 -1
  7. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/oracles/scope_cards.yaml +23 -0
  8. pen_stack-5.9.0/docs/digital_twin.md +55 -0
  9. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/__init__.py +1 -1
  10. pen_stack-5.9.0/pen_stack/oracles/vcell.py +51 -0
  11. pen_stack-5.9.0/pen_stack/twin/__init__.py +14 -0
  12. pen_stack-5.9.0/pen_stack/twin/calibrate.py +61 -0
  13. pen_stack-5.9.0/pen_stack/twin/mechanistic.py +37 -0
  14. pen_stack-5.9.0/pen_stack/twin/outcome.py +84 -0
  15. pen_stack-5.9.0/pen_stack/validate/outcome_prediction.py +76 -0
  16. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack.egg-info/PKG-INFO +30 -9
  17. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack.egg-info/SOURCES.txt +15 -0
  18. pen_stack-5.9.0/prereg/SHA256_LOCK_ws_mech.json +8 -0
  19. pen_stack-5.9.0/prereg/SHA256_LOCK_ws_outcome.json +8 -0
  20. pen_stack-5.9.0/prereg/SHA256_LOCK_ws_twincal.json +8 -0
  21. pen_stack-5.9.0/prereg/SHA256_LOCK_ws_vcell.json +8 -0
  22. pen_stack-5.9.0/prereg/ws_mech.yaml +16 -0
  23. pen_stack-5.9.0/prereg/ws_outcome.yaml +19 -0
  24. pen_stack-5.9.0/prereg/ws_twincal.yaml +20 -0
  25. pen_stack-5.9.0/prereg/ws_vcell.yaml +20 -0
  26. {pen_stack-5.8.0 → pen_stack-5.9.0}/pyproject.toml +1 -1
  27. {pen_stack-5.8.0 → pen_stack-5.9.0}/LICENSE +0 -0
  28. {pen_stack-5.8.0 → pen_stack-5.9.0}/MANIFEST.in +0 -0
  29. {pen_stack-5.8.0 → pen_stack-5.9.0}/bench/run.py +0 -0
  30. {pen_stack-5.8.0 → pen_stack-5.9.0}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
  31. {pen_stack-5.8.0 → pen_stack-5.9.0}/benchmarks/genome_writing_bench/README.md +0 -0
  32. {pen_stack-5.8.0 → pen_stack-5.9.0}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  33. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/antipeg.yaml +0 -0
  34. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/atlas_families.yaml +0 -0
  35. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/bridge_offtarget_profile.yaml +0 -0
  36. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/capsid_epitope_oracle.yaml +0 -0
  37. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/capsid_sequences.fasta +0 -0
  38. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/cargo_polish.yaml +0 -0
  39. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/cell_types.yaml +0 -0
  40. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/datasets.yaml +0 -0
  41. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/delivery_constraints.yaml +0 -0
  42. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/delivery_rules.yaml +0 -0
  43. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/delivery_vehicles.yaml +0 -0
  44. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/gates_v3.yaml +0 -0
  45. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/genotoxicity_oracle.yaml +0 -0
  46. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/gsh_validated_heldout.yaml +0 -0
  47. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/intent_weights.yaml +0 -0
  48. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/known_unknowns.yaml +0 -0
  49. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/llm.yaml +0 -0
  50. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/monitor_queries.yaml +0 -0
  51. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/rules/delivery.yaml +0 -0
  52. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/rules/fold.yaml +0 -0
  53. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/rules/multiplex.yaml +0 -0
  54. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/rules/payload.yaml +0 -0
  55. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/rules/reachability.yaml +0 -0
  56. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/safety/hazard_registry.yaml +0 -0
  57. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/safety/policy.yaml +0 -0
  58. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/safety/probes.yaml +0 -0
  59. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/score_axes.yaml +0 -0
  60. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/seroprevalence.yaml +0 -0
  61. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/target_sites.yaml +0 -0
  62. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/universe_crosswalk.yaml +0 -0
  63. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/write_types.yaml +0 -0
  64. {pen_stack-5.8.0 → pen_stack-5.9.0}/configs/wtkb_curated.yaml +0 -0
  65. {pen_stack-5.8.0 → pen_stack-5.9.0}/data/curated/bridge_offtarget_energetics.json +0 -0
  66. {pen_stack-5.8.0 → pen_stack-5.9.0}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  67. {pen_stack-5.8.0 → pen_stack-5.9.0}/data/curated/gene_coords.parquet +0 -0
  68. {pen_stack-5.8.0 → pen_stack-5.9.0}/data/curated/unified_editor_universe.parquet +0 -0
  69. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/BACKLOG.md +0 -0
  70. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/DEPLOY.md +0 -0
  71. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/INFRA.md +0 -0
  72. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/MCP.md +0 -0
  73. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/RELEASING.md +0 -0
  74. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/REPRO.md +0 -0
  75. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/agent.md +0 -0
  76. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/alphagenome_feasibility.md +0 -0
  77. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/benchmark_circularity.md +0 -0
  78. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/biosecurity.md +0 -0
  79. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/cards/atlas.md +0 -0
  80. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/cards/durability.md +0 -0
  81. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/cards/safety.md +0 -0
  82. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/co_scientist.md +0 -0
  83. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/delivery.md +0 -0
  84. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/delivery_immunology.md +0 -0
  85. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/dissemination.md +0 -0
  86. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/environment.md +0 -0
  87. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/generative_design.md +0 -0
  88. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/index.md +0 -0
  89. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/mechanistic_constraints.md +0 -0
  90. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/oracles.md +0 -0
  91. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/positioning.md +0 -0
  92. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/private_data_formats.md +0 -0
  93. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/quickstart.md +0 -0
  94. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/responsible_use.md +0 -0
  95. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/rules.md +0 -0
  96. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/scope.md +0 -0
  97. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/scorecard.md +0 -0
  98. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/tutorials/compare-families.md +0 -0
  99. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/tutorials/score-deliverability.md +0 -0
  100. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/tutorials/where-can-i-write.md +0 -0
  101. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  102. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/uncertainty.md +0 -0
  103. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/verify.md +0 -0
  104. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/world_model.md +0 -0
  105. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/writer_verification.md +0 -0
  106. {pen_stack-5.8.0 → pen_stack-5.9.0}/docs/wtkb.md +0 -0
  107. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/_resources.py +0 -0
  108. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/adapt/__init__.py +0 -0
  109. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/adapt/finetune.py +0 -0
  110. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/adapt/ingest.py +0 -0
  111. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/adapt/pipeline.py +0 -0
  112. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/adapt/recalibrate.py +0 -0
  113. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/adapt/report.py +0 -0
  114. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/__init__.py +0 -0
  115. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/cite.py +0 -0
  116. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/co_scientist.py +0 -0
  117. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/epistemic.py +0 -0
  118. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/guardrails.py +0 -0
  119. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/mcp_server.py +0 -0
  120. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/orchestrator.py +0 -0
  121. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/orchestrator_live.py +0 -0
  122. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/pen_agent.py +0 -0
  123. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/scope.py +0 -0
  124. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/agent/tools.py +0 -0
  125. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/__init__.py +0 -0
  126. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/build_wtkb.py +0 -0
  127. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/crosslink.py +0 -0
  128. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/expand.py +0 -0
  129. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/schema.py +0 -0
  130. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/scorecard.py +0 -0
  131. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/universe.py +0 -0
  132. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/variant_propose.py +0 -0
  133. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/atlas/writer_verify.py +0 -0
  134. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/__init__.py +0 -0
  135. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/activity.py +0 -0
  136. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/cli.py +0 -0
  137. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/fold_qc.py +0 -0
  138. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/guide_qc.py +0 -0
  139. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/ingest.py +0 -0
  140. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/offtarget.py +0 -0
  141. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/offtarget_energetics.py +0 -0
  142. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/ortholog_screen.py +0 -0
  143. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/bridge/pipeline.py +0 -0
  144. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/cli.py +0 -0
  145. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/data/__init__.py +0 -0
  146. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/data/encode.py +0 -0
  147. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/data/genome.py +0 -0
  148. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/data/ingest_chromatin.py +0 -0
  149. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/data/ingest_integration.py +0 -0
  150. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/data/ingest_safety_annot.py +0 -0
  151. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/data/ingest_trip.py +0 -0
  152. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/design/__init__.py +0 -0
  153. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/design/generate.py +0 -0
  154. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/design/pareto.py +0 -0
  155. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/design/space.py +0 -0
  156. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/env/__init__.py +0 -0
  157. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/env/genome_writing_env.py +0 -0
  158. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/env/policies.py +0 -0
  159. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/graph/__init__.py +0 -0
  160. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/graph/build.py +0 -0
  161. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/graph/cell_types.py +0 -0
  162. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/graph/ingest.py +0 -0
  163. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/graph/query.py +0 -0
  164. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/graph/schema.py +0 -0
  165. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/mech/__init__.py +0 -0
  166. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/mech/classify_atlas.py +0 -0
  167. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/mech/whitelist.py +0 -0
  168. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/monitor/__init__.py +0 -0
  169. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/monitor/europepmc.py +0 -0
  170. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/monitor/run.py +0 -0
  171. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/monitor/triage.py +0 -0
  172. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/__init__.py +0 -0
  173. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/cache.py +0 -0
  174. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/energetics.py +0 -0
  175. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/genome.py +0 -0
  176. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/protein_design.py +0 -0
  177. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/rna.py +0 -0
  178. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/schema.py +0 -0
  179. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/oracles/structure.py +0 -0
  180. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/__init__.py +0 -0
  181. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/antipeg_oracle.py +0 -0
  182. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
  183. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/cargo.py +0 -0
  184. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/cargo_polish.py +0 -0
  185. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/delivery.py +0 -0
  186. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/delivery_constraints.py +0 -0
  187. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/delivery_immunology.py +0 -0
  188. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/delivery_vehicles.py +0 -0
  189. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/genotoxicity_oracle.py +0 -0
  190. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/immune_profile.py +0 -0
  191. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/innate_sensing.py +0 -0
  192. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/multiplex.py +0 -0
  193. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/optimize.py +0 -0
  194. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/pipeline.py +0 -0
  195. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/report.py +0 -0
  196. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/router.py +0 -0
  197. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/seroprevalence_oracle.py +0 -0
  198. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/planner/target_site.py +0 -0
  199. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rag/__init__.py +0 -0
  200. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rag/index.py +0 -0
  201. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rag/llm.py +0 -0
  202. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rag/qa.py +0 -0
  203. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rules/__init__.py +0 -0
  204. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rules/evaluators.py +0 -0
  205. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rules/loader.py +0 -0
  206. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rules/schema.py +0 -0
  207. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/rules/solver.py +0 -0
  208. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/safety/__init__.py +0 -0
  209. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/safety/audit.py +0 -0
  210. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/safety/gate.py +0 -0
  211. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/safety/policy.py +0 -0
  212. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/safety/redteam.py +0 -0
  213. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/safety/registry.py +0 -0
  214. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/safety/screen.py +0 -0
  215. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/score/__init__.py +0 -0
  216. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/score/recalibrate.py +0 -0
  217. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/score/therapeutic.py +0 -0
  218. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/server/__init__.py +0 -0
  219. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/server/api.py +0 -0
  220. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/ui/__init__.py +0 -0
  221. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/ui/app.py +0 -0
  222. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/__init__.py +0 -0
  223. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/adapt_demo.py +0 -0
  224. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/agent_eval.py +0 -0
  225. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  226. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
  227. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/bench_graph_tasks.py +0 -0
  228. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/bench_rule_tasks.py +0 -0
  229. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/bench_trust_tasks.py +0 -0
  230. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  231. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  232. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/cargo_directionality.py +0 -0
  233. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/durability_baselines.py +0 -0
  234. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/forward_hypotheses.py +0 -0
  235. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/generative_design.py +0 -0
  236. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/guide_qc_demo.py +0 -0
  237. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/immune_calibration.py +0 -0
  238. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/intent_specification.py +0 -0
  239. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  240. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  241. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/outcome_calibration.py +0 -0
  242. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/paper3_benchmark.py +0 -0
  243. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/paper4_real_validation.py +0 -0
  244. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/paper4_validation.py +0 -0
  245. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/safety_screening.py +0 -0
  246. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/selective_prediction.py +0 -0
  247. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/seq_vs_measured.py +0 -0
  248. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/target_site_controls.py +0 -0
  249. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/uncertainty_eval.py +0 -0
  250. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/ungrounded_baseline.py +0 -0
  251. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/within_locus_ranking.py +0 -0
  252. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/validate/writer_recovery.py +0 -0
  253. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/verify/__init__.py +0 -0
  254. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/verify/schema.py +0 -0
  255. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/verify/service.py +0 -0
  256. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/__init__.py +0 -0
  257. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/chromatin_seq.py +0 -0
  258. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/durability.py +0 -0
  259. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/export_tracks.py +0 -0
  260. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/features.py +0 -0
  261. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/gsh_baseline.py +0 -0
  262. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/mesh_features.py +0 -0
  263. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/ood.py +0 -0
  264. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/providers.py +0 -0
  265. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/safety.py +0 -0
  266. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/structure3d.py +0 -0
  267. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/uncertainty.py +0 -0
  268. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack/wgenome/writability.py +0 -0
  269. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack.egg-info/dependency_links.txt +0 -0
  270. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack.egg-info/entry_points.txt +0 -0
  271. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack.egg-info/requires.txt +0 -0
  272. {pen_stack-5.8.0 → pen_stack-5.9.0}/pen_stack.egg-info/top_level.txt +0 -0
  273. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_phase0.json +0 -0
  274. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  275. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_phase2.json +0 -0
  276. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_phase3.json +0 -0
  277. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_a.json +0 -0
  278. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  279. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_b.json +0 -0
  280. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  281. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  282. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  283. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  284. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_c.json +0 -0
  285. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  286. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_calib.json +0 -0
  287. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_cite.json +0 -0
  288. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_crit.json +0 -0
  289. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  290. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_d.json +0 -0
  291. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_e.json +0 -0
  292. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_env.json +0 -0
  293. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  294. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
  295. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_f.json +0 -0
  296. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_g.json +0 -0
  297. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_gen.json +0 -0
  298. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
  299. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  300. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_h.json +0 -0
  301. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_immune.json +0 -0
  302. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_innate.json +0 -0
  303. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  304. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  305. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_o.json +0 -0
  306. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_orch.json +0 -0
  307. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
  308. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_peg.json +0 -0
  309. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_plan.json +0 -0
  310. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_policy.json +0 -0
  311. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_profile.json +0 -0
  312. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_r.json +0 -0
  313. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
  314. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_route.json +0 -0
  315. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_screen.json +0 -0
  316. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
  317. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  318. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_v.json +0 -0
  319. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  320. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/paper1.yaml +0 -0
  321. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/paper2.yaml +0 -0
  322. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/paper3.yaml +0 -0
  323. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/paper4.yaml +0 -0
  324. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/phase0.yaml +0 -0
  325. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_a.yaml +0 -0
  326. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_atlas.yaml +0 -0
  327. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_b.yaml +0 -0
  328. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_ba.yaml +0 -0
  329. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_ba_v33.yaml +0 -0
  330. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_ba_v45.yaml +0 -0
  331. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_bench.yaml +0 -0
  332. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_c.yaml +0 -0
  333. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_cal.yaml +0 -0
  334. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_calib.yaml +0 -0
  335. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_cite.yaml +0 -0
  336. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_crit.yaml +0 -0
  337. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_ct.yaml +0 -0
  338. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_d.yaml +0 -0
  339. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_e.yaml +0 -0
  340. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_env.yaml +0 -0
  341. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_ep.yaml +0 -0
  342. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_epitope.yaml +0 -0
  343. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_f.yaml +0 -0
  344. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_g.yaml +0 -0
  345. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_gen.yaml +0 -0
  346. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_genotox.yaml +0 -0
  347. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_graph.yaml +0 -0
  348. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_h.yaml +0 -0
  349. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_immune.yaml +0 -0
  350. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_innate.yaml +0 -0
  351. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_mc.yaml +0 -0
  352. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_mon.yaml +0 -0
  353. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_o.yaml +0 -0
  354. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_orch.yaml +0 -0
  355. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_pareto.yaml +0 -0
  356. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_peg.yaml +0 -0
  357. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_plan.yaml +0 -0
  358. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_policy.yaml +0 -0
  359. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_profile.yaml +0 -0
  360. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_r.yaml +0 -0
  361. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_redteam.yaml +0 -0
  362. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_route.yaml +0 -0
  363. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_screen.yaml +0 -0
  364. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_seroprev.yaml +0 -0
  365. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_uq.yaml +0 -0
  366. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_v.yaml +0 -0
  367. {pen_stack-5.8.0 → pen_stack-5.9.0}/prereg/ws_wv.yaml +0 -0
  368. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p1_build_atlas.py +0 -0
  369. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p1_build_durability.py +0 -0
  370. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p1_export_tracks.py +0 -0
  371. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p1_safety_concordance.py +0 -0
  372. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p1_train_safety.py +0 -0
  373. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p1_validation_report.py +0 -0
  374. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p2_build_atlas.py +0 -0
  375. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p3_benchmark_report.py +0 -0
  376. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p4_genome_scan.py +0 -0
  377. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p52_build_genotox_oracle.py +0 -0
  378. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/p53_build_epitope_oracle.py +0 -0
  379. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/ws_b_report.py +0 -0
  380. {pen_stack-5.8.0 → pen_stack-5.9.0}/scripts/ws_c_report.py +0 -0
  381. {pen_stack-5.8.0 → pen_stack-5.9.0}/setup.cfg +0 -0
@@ -3,6 +3,42 @@
3
3
  All notable changes to PEN-STACK are documented here. This file follows
4
4
  [Keep a Changelog](https://keepachangelog.com/) and the program's phase structure.
5
5
 
6
+ ## [5.9.0] - 2026-06-11 - v5.9 release: The Digital Twin (calibrated outcome prediction)
7
+
8
+ **Closed-Loop arc, Cycle 3 of 7.** The missing layer: *what does the cell do after the write?* — predicted with
9
+ calibrated honesty. The twin computes what mechanism allows, adds an in-distribution virtual-cell estimate
10
+ (OOD-gated), screens immune outcome from the v5.6 profile, and is explicit about its boundary at phenotype.
11
+ Workstreams WS-{VCELL,MECH,OUTCOME,CAL}, SHA-locked.
12
+
13
+ ### Added
14
+ - **WS-VCELL** — `pen_stack/oracles/vcell.py` + scope cards `state`/`scgpt`: `predict_response(cell_state,
15
+ perturbation, model)` wraps **Arc STATE** / **scGPT** under the v4.0 `OracleResult` contract. A
16
+ perturbation-response prediction is a **candidate**, OOD-gated (a context outside the documented envelope →
17
+ `extrapolating`), cached/deferred (value `None` when absent — never fabricated). Encodes the field's own result
18
+ (Arc Virtual Cell Challenge): perturbation models don't yet consistently beat naive baselines.
19
+ - **WS-MECH** — `pen_stack/twin/mechanistic.py`: `cassette_expression` = `promoter_strength × copy_number ×
20
+ accessibility` (closed-form steady state); assumptions + scope flags attached; **physics where computable, NOT
21
+ a phenotype**.
22
+ - **WS-OUTCOME** — `pen_stack/twin/outcome.py`: `predict_outcome(design, cell_state)` fuses mechanism +
23
+ in-distribution virtual-cell response + the v5.6 immune profile into one prediction with an interval that
24
+ **widens under OOD**, an immune-outcome dimension, and an explicit **phenotype / in-vivo-magnitude boundary**.
25
+ In-vivo durability may be **conditioned on the grounded pre-existing-NAb axis** (no invented immune numbers);
26
+ `output_kind="candidate"`.
27
+ - **WS-CAL** — `pen_stack/twin/calibrate.py`: `calibrate_outcome(...)` reports calibration **two-sided** — interval
28
+ coverage + a bootstrap CI on the MAE gap vs a naive mean baseline; the twin "beats" naive **only when the CI
29
+ excludes zero**, else the negative is reported verbatim; abstains at `N < 3`.
30
+ - **WS-BENCH** — bench **v0.3.5**: new `outcome_prediction` hard-gate task (`pen_stack/validate/outcome_prediction.py`)
31
+ — the gate is the twin's **honesty properties** (two-sided calibration + OOD widening + immune dimension +
32
+ phenotype out-of-scope), which an overconfident predictor fails by construction; twin-vs-naive skill is reported
33
+ informationally on a labelled synthetic stream (no public perturbation-outcome calibration set exists).
34
+ - Docs: `docs/digital_twin.md`; prereg `ws_{vcell,mech,outcome,twincal}` + SHA locks; deposit `phase_5.9/`.
35
+
36
+ ### Notes
37
+ - The twin is a **hypothesis engine, not an oracle of truth**: predictions are candidates with intervals;
38
+ phenotype, in-vivo behaviour, immunogenicity *magnitude*, and durability beyond the computable stay
39
+ scope-flagged. The interval is a heuristic band, **not** a trained conformal interval (no public outcome
40
+ calibration set). Immune-outcome is sourced from v5.6, never invented.
41
+
6
42
  ## [5.8.0] - 2026-06-11 - v5.8 release: The Live Agent & Generative Designer
7
43
 
8
44
  **Closed-Loop arc, Cycle 2 of 7.** PEN-STACK turns from a *checker* into a grounded *designer*: it generates
@@ -1,7 +1,7 @@
1
1
  cff-version: 1.2.0
2
2
  message: "If you use PEN-STACK, please cite it as below."
3
3
  title: "PEN-STACK: open infrastructure for genome writing"
4
- version: 5.8.0
4
+ version: 5.9.0
5
5
  date-released: 2026-06-11
6
6
  authors:
7
7
  - family-names: "Mahaboob Ali"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 5.8.0
3
+ Version: 5.9.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
@@ -90,12 +90,12 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
90
90
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
91
91
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
92
92
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
93
- [![Version](https://img.shields.io/badge/version-5.8.0-blue.svg)](CHANGELOG.md)
94
- [![Tests](https://img.shields.io/badge/tests-317%20passing-success.svg)](tests/)
93
+ [![Version](https://img.shields.io/badge/version-5.9.0-blue.svg)](CHANGELOG.md)
94
+ [![Tests](https://img.shields.io/badge/tests-324%20passing-success.svg)](tests/)
95
95
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
96
96
  [![Runtime: Docker](https://img.shields.io/badge/runtime-docker-2496ED.svg)](docker/)
97
97
  [![Validation: pre-registered](https://img.shields.io/badge/validation-pre--registered-critical.svg)](prereg/)
98
- [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.4-6f42c1.svg)](benchmarks/genome_writing_bench/)
98
+ [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.5-6f42c1.svg)](benchmarks/genome_writing_bench/)
99
99
 
100
100
  **Built on five prior, separately published repositories:**
101
101
 
@@ -134,6 +134,25 @@ Two questions gate every genome-writing project, and before PEN-STACK no resourc
134
134
  Everything is built on bulk-downloadable public data, runs on a single GPU, and is validated **blind** against
135
135
  a pre-registered, honest baseline before release.
136
136
 
137
+ ## What is new in v5.9 — The Digital Twin (calibrated outcome prediction)
138
+
139
+ v5.9 (**Closed-Loop arc, Cycle 3 of 7**) adds the missing layer — *what does the cell do after the write?* —
140
+ predicted with calibrated honesty. The twin computes what mechanism allows, adds an in-distribution virtual-cell
141
+ estimate (OOD-gated), screens immune outcome from the v5.6 profile, and is explicit about its boundary at
142
+ phenotype. A **hypothesis engine, not an oracle of truth**.
143
+
144
+ | Workstream | What it adds | Result |
145
+ |---|---|---|
146
+ | **VCELL** | `oracles/vcell.py` + scope cards `state`/`scgpt` | Arc STATE / scGPT under the OracleResult contract; perturbation prediction is a **candidate**, **OOD-gated**, deferred value never fabricated |
147
+ | **MECH** | `twin/mechanistic.py` | `cassette_expression` = promoter × copy × accessibility (closed form); **physics where computable, NOT a phenotype** |
148
+ | **OUTCOME** | `twin/outcome.py` | fuses mechanism + in-dist VC + v5.6 immune; interval **widens under OOD**; in-vivo durability conditioned on the **grounded NAb axis**; phenotype/in-vivo-magnitude scope-flagged |
149
+ | **CAL** | `twin/calibrate.py` | calibration reported **two-sided** (coverage + MAE-gap-vs-naive bootstrap CI); beats naive **only if CI excludes 0**, else the negative is reported |
150
+ | **BENCH** | bench **v0.3.5** `outcome_prediction` hard gate | gate = the twin's honesty properties (two-sided cal + OOD widening + immune dim + phenotype out-of-scope); an overconfident predictor fails by construction |
151
+
152
+ The interval is a **heuristic band, not a trained conformal interval** (no public perturbation-outcome calibration
153
+ set; Arc's Virtual Cell Challenge shows models don't yet consistently beat naive baselines). See
154
+ [`docs/digital_twin.md`](docs/digital_twin.md) and `prereg/ws_{vcell,mech,outcome,twincal}.yaml`.
155
+
137
156
  ## What is new in v5.8 — The Live Agent & Generative Designer
138
157
 
139
158
  v5.8 (**Closed-Loop arc, Cycle 2 of 7**) turns PEN-STACK from a *checker* into a grounded *designer*: it
@@ -599,11 +618,12 @@ pen-stack/
599
618
  │ ├── agent/ agentic platform: tools / orchestrator / pen_agent / mcp_server / guardrails; v5.0 co_scientist + cite (multi-strategy, self-critique, cited rationale, scope ledger); v5.8 orchestrator_live (live, cache-replayable, generate→oracle→verify→refine)
600
619
  │ │ + v3.2 epistemic (3-tier status) / scope (known-unknowns matcher)
601
620
  │ ├── graph/ v4.5 living world-model knowledge graph (schema/build/query/ingest/cell_types); typed provenanced edges; gated living loop (propose-only)
602
- │ ├── oracles/ v4.0 L1 oracle mesh: OracleResult contract + adapters (genome/structure/protein_design/rna/energetics) over the foundation models; version-pinned cache; v5.2-5.6 delivery-immunology scope cards (delivery_genotoxicity/capsid_epitope/innate_sensing/seroprevalence/antipeg)
621
+ │ ├── oracles/ v4.0 L1 oracle mesh: OracleResult contract + adapters (genome/structure/protein_design/rna/energetics) over the foundation models; version-pinned cache; v5.2-5.6 delivery-immunology scope cards (delivery_genotoxicity/capsid_epitope/innate_sensing/seroprevalence/antipeg); v5.9 vcell (Arc STATE/scGPT, OOD-gated, output_kind=candidate)
603
622
  │ ├── rules/ v3.3 machine-readable rules engine (schema/evaluators/loader/solver) over configs/rules/*.yaml
604
623
  │ ├── verify/ v3.3 verification service: verify(design) -> Verdict (legal+reasons+confidence+scope; v4.0 writer_critique; v5.1 delivery_profile; v5.6 immune_profile per-axis vector; v5.7 safety SafetyVerdict)
605
624
  │ ├── safety/ v5.7 the Guardian: biosecurity/dual-use gate (registry/screen/policy/gate/audit/redteam); runs first in verify(); refuse short-circuits; tamper-evident audit
606
625
  │ ├── design/ v5.8 generative designer: space (candidate_space) / generate (verifier-as-discriminator; hazardous+illegal discarded) / pareto (frontier w/ grounded v5.6 immune axis)
626
+ │ ├── twin/ v5.9 digital twin: mechanistic (cassette expression, closed-form) / outcome (fuse mech+vcell+v5.6 immune; OOD widens interval; phenotype-bounded) / calibrate (honest two-sided)
607
627
  │ ├── adapt/ local recalibration / private-data adaptation behind a gate (v3.1, WS-F)
608
628
  │ ├── env/ v3.4 full Gymnasium environment over router+verifier (genome_writing_env + policies; [env] extra)
609
629
  │ ├── monitor/ PEN-MONITOR living database (Europe PMC)
@@ -615,12 +635,13 @@ pen-stack/
615
635
  │ │ v3.3 bench_rule_tasks (T12) / v3.4 bench_writetype_tasks + bench_adversarial_tasks (T13-16) + outcome_calibration /
616
636
  │ │ v5.6 immune_calibration (proxy-vs-observed; labels each axis validated-or-proxy, two-sided) /
617
637
  │ │ v5.7 safety_screening (the Guardian hard-gate: benign 0-false-refusal · hazards refused/escalated · evasions never clear) /
618
- │ │ v5.8 generative_design (verifier-as-discriminator hard-gate: hazardous+illegal discarded; survivors calibrated+immune; grounded-immune Pareto)
638
+ │ │ v5.8 generative_design (verifier-as-discriminator hard-gate: hazardous+illegal discarded; survivors calibrated+immune; grounded-immune Pareto) /
639
+ │ │ v5.9 outcome_prediction (digital-twin hard-gate: two-sided calibration + OOD widening + immune dim + phenotype out-of-scope)
619
640
  │ ├── data/ ingestion (genome, chromatin, integration, TRIP, safety annotations)
620
641
  │ ├── server/api.py FastAPI REST (atlas, crosslink, writable, plan, bridge, ask)
621
642
  │ ├── ui/app.py Streamlit web app (16 pages; v3.2 PEN-Agent shows confidence + epistemic status)
622
643
  │ └── cli.py unified CLI
623
- ├── benchmarks/genome_writing_bench/ Genome-Writing Bench v0.3.4 (T1-T16 + co_scientist + safety_screening + generative_design; tasks / harness / solvers / LEADERBOARD / SHAs)
644
+ ├── benchmarks/genome_writing_bench/ Genome-Writing Bench v0.3.5 (T1-T16 + co_scientist + safety_screening + generative_design + outcome_prediction; tasks / harness / solvers / LEADERBOARD / SHAs)
624
645
  ├── bench/run.py one-command bench entrypoint (--agent, --verify)
625
646
  ├── scripts/ reproducible pipeline drivers (p1_*, p2_*, p4_*, p52/p53 delivery-immunology oracle builds, ws_*_report)
626
647
  ├── configs/ pinned datasets + thresholds + curation (YAML); v3.2 known_unknowns /
@@ -629,10 +650,10 @@ pen-stack/
629
650
  │ seroprevalence / antipeg + oracles/scope_cards (+ v5.6 known_unknowns:
630
651
  │ cd4_mhcii_help / preexisting_capsid_tcell / complement_carpa);
631
652
  │ v5.7 safety/{hazard_registry,policy,probes} (Guardian; function/family/taxon-level only)
632
- ├── prereg/ SHA-locked success criteria (paper1..4 + ws_a..ws_h + v3.2-v5.8 ws_{uq,ep,mc,ba,
653
+ ├── prereg/ SHA-locked success criteria (paper1..4 + ws_a..ws_h + v3.2-v5.9 ws_{uq,ep,mc,ba,
633
654
  │ r,v,route,env,bench,cal,o,wv,atlas,graph,mon,ct,plan,crit,cite,immune,
634
655
  │ genotox,epitope,innate,seroprev,peg,calib,profile,screen,policy,redteam,
635
- │ gen,pareto,orch} + SHA256 locks)
656
+ │ gen,pareto,orch,vcell,mech,outcome,twincal} + SHA256 locks)
636
657
  ├── data/curated/ small committed tables (universe, gene coords, measured bridge profile,
637
658
  │ v3.2 bridge_offtarget_energetics.json)
638
659
  ├── data/llm_bench_cache/ 28 cached ungrounded-LLM transcripts (T7, offline/CI replay)
@@ -15,12 +15,12 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
15
15
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
16
16
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
17
17
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
18
- [![Version](https://img.shields.io/badge/version-5.8.0-blue.svg)](CHANGELOG.md)
19
- [![Tests](https://img.shields.io/badge/tests-317%20passing-success.svg)](tests/)
18
+ [![Version](https://img.shields.io/badge/version-5.9.0-blue.svg)](CHANGELOG.md)
19
+ [![Tests](https://img.shields.io/badge/tests-324%20passing-success.svg)](tests/)
20
20
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
21
21
  [![Runtime: Docker](https://img.shields.io/badge/runtime-docker-2496ED.svg)](docker/)
22
22
  [![Validation: pre-registered](https://img.shields.io/badge/validation-pre--registered-critical.svg)](prereg/)
23
- [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.4-6f42c1.svg)](benchmarks/genome_writing_bench/)
23
+ [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.5-6f42c1.svg)](benchmarks/genome_writing_bench/)
24
24
 
25
25
  **Built on five prior, separately published repositories:**
26
26
 
@@ -59,6 +59,25 @@ Two questions gate every genome-writing project, and before PEN-STACK no resourc
59
59
  Everything is built on bulk-downloadable public data, runs on a single GPU, and is validated **blind** against
60
60
  a pre-registered, honest baseline before release.
61
61
 
62
+ ## What is new in v5.9 — The Digital Twin (calibrated outcome prediction)
63
+
64
+ v5.9 (**Closed-Loop arc, Cycle 3 of 7**) adds the missing layer — *what does the cell do after the write?* —
65
+ predicted with calibrated honesty. The twin computes what mechanism allows, adds an in-distribution virtual-cell
66
+ estimate (OOD-gated), screens immune outcome from the v5.6 profile, and is explicit about its boundary at
67
+ phenotype. A **hypothesis engine, not an oracle of truth**.
68
+
69
+ | Workstream | What it adds | Result |
70
+ |---|---|---|
71
+ | **VCELL** | `oracles/vcell.py` + scope cards `state`/`scgpt` | Arc STATE / scGPT under the OracleResult contract; perturbation prediction is a **candidate**, **OOD-gated**, deferred value never fabricated |
72
+ | **MECH** | `twin/mechanistic.py` | `cassette_expression` = promoter × copy × accessibility (closed form); **physics where computable, NOT a phenotype** |
73
+ | **OUTCOME** | `twin/outcome.py` | fuses mechanism + in-dist VC + v5.6 immune; interval **widens under OOD**; in-vivo durability conditioned on the **grounded NAb axis**; phenotype/in-vivo-magnitude scope-flagged |
74
+ | **CAL** | `twin/calibrate.py` | calibration reported **two-sided** (coverage + MAE-gap-vs-naive bootstrap CI); beats naive **only if CI excludes 0**, else the negative is reported |
75
+ | **BENCH** | bench **v0.3.5** `outcome_prediction` hard gate | gate = the twin's honesty properties (two-sided cal + OOD widening + immune dim + phenotype out-of-scope); an overconfident predictor fails by construction |
76
+
77
+ The interval is a **heuristic band, not a trained conformal interval** (no public perturbation-outcome calibration
78
+ set; Arc's Virtual Cell Challenge shows models don't yet consistently beat naive baselines). See
79
+ [`docs/digital_twin.md`](docs/digital_twin.md) and `prereg/ws_{vcell,mech,outcome,twincal}.yaml`.
80
+
62
81
  ## What is new in v5.8 — The Live Agent & Generative Designer
63
82
 
64
83
  v5.8 (**Closed-Loop arc, Cycle 2 of 7**) turns PEN-STACK from a *checker* into a grounded *designer*: it
@@ -524,11 +543,12 @@ pen-stack/
524
543
  │ ├── agent/ agentic platform: tools / orchestrator / pen_agent / mcp_server / guardrails; v5.0 co_scientist + cite (multi-strategy, self-critique, cited rationale, scope ledger); v5.8 orchestrator_live (live, cache-replayable, generate→oracle→verify→refine)
525
544
  │ │ + v3.2 epistemic (3-tier status) / scope (known-unknowns matcher)
526
545
  │ ├── graph/ v4.5 living world-model knowledge graph (schema/build/query/ingest/cell_types); typed provenanced edges; gated living loop (propose-only)
527
- │ ├── oracles/ v4.0 L1 oracle mesh: OracleResult contract + adapters (genome/structure/protein_design/rna/energetics) over the foundation models; version-pinned cache; v5.2-5.6 delivery-immunology scope cards (delivery_genotoxicity/capsid_epitope/innate_sensing/seroprevalence/antipeg)
546
+ │ ├── oracles/ v4.0 L1 oracle mesh: OracleResult contract + adapters (genome/structure/protein_design/rna/energetics) over the foundation models; version-pinned cache; v5.2-5.6 delivery-immunology scope cards (delivery_genotoxicity/capsid_epitope/innate_sensing/seroprevalence/antipeg); v5.9 vcell (Arc STATE/scGPT, OOD-gated, output_kind=candidate)
528
547
  │ ├── rules/ v3.3 machine-readable rules engine (schema/evaluators/loader/solver) over configs/rules/*.yaml
529
548
  │ ├── verify/ v3.3 verification service: verify(design) -> Verdict (legal+reasons+confidence+scope; v4.0 writer_critique; v5.1 delivery_profile; v5.6 immune_profile per-axis vector; v5.7 safety SafetyVerdict)
530
549
  │ ├── safety/ v5.7 the Guardian: biosecurity/dual-use gate (registry/screen/policy/gate/audit/redteam); runs first in verify(); refuse short-circuits; tamper-evident audit
531
550
  │ ├── design/ v5.8 generative designer: space (candidate_space) / generate (verifier-as-discriminator; hazardous+illegal discarded) / pareto (frontier w/ grounded v5.6 immune axis)
551
+ │ ├── twin/ v5.9 digital twin: mechanistic (cassette expression, closed-form) / outcome (fuse mech+vcell+v5.6 immune; OOD widens interval; phenotype-bounded) / calibrate (honest two-sided)
532
552
  │ ├── adapt/ local recalibration / private-data adaptation behind a gate (v3.1, WS-F)
533
553
  │ ├── env/ v3.4 full Gymnasium environment over router+verifier (genome_writing_env + policies; [env] extra)
534
554
  │ ├── monitor/ PEN-MONITOR living database (Europe PMC)
@@ -540,12 +560,13 @@ pen-stack/
540
560
  │ │ v3.3 bench_rule_tasks (T12) / v3.4 bench_writetype_tasks + bench_adversarial_tasks (T13-16) + outcome_calibration /
541
561
  │ │ v5.6 immune_calibration (proxy-vs-observed; labels each axis validated-or-proxy, two-sided) /
542
562
  │ │ v5.7 safety_screening (the Guardian hard-gate: benign 0-false-refusal · hazards refused/escalated · evasions never clear) /
543
- │ │ v5.8 generative_design (verifier-as-discriminator hard-gate: hazardous+illegal discarded; survivors calibrated+immune; grounded-immune Pareto)
563
+ │ │ v5.8 generative_design (verifier-as-discriminator hard-gate: hazardous+illegal discarded; survivors calibrated+immune; grounded-immune Pareto) /
564
+ │ │ v5.9 outcome_prediction (digital-twin hard-gate: two-sided calibration + OOD widening + immune dim + phenotype out-of-scope)
544
565
  │ ├── data/ ingestion (genome, chromatin, integration, TRIP, safety annotations)
545
566
  │ ├── server/api.py FastAPI REST (atlas, crosslink, writable, plan, bridge, ask)
546
567
  │ ├── ui/app.py Streamlit web app (16 pages; v3.2 PEN-Agent shows confidence + epistemic status)
547
568
  │ └── cli.py unified CLI
548
- ├── benchmarks/genome_writing_bench/ Genome-Writing Bench v0.3.4 (T1-T16 + co_scientist + safety_screening + generative_design; tasks / harness / solvers / LEADERBOARD / SHAs)
569
+ ├── benchmarks/genome_writing_bench/ Genome-Writing Bench v0.3.5 (T1-T16 + co_scientist + safety_screening + generative_design + outcome_prediction; tasks / harness / solvers / LEADERBOARD / SHAs)
549
570
  ├── bench/run.py one-command bench entrypoint (--agent, --verify)
550
571
  ├── scripts/ reproducible pipeline drivers (p1_*, p2_*, p4_*, p52/p53 delivery-immunology oracle builds, ws_*_report)
551
572
  ├── configs/ pinned datasets + thresholds + curation (YAML); v3.2 known_unknowns /
@@ -554,10 +575,10 @@ pen-stack/
554
575
  │ seroprevalence / antipeg + oracles/scope_cards (+ v5.6 known_unknowns:
555
576
  │ cd4_mhcii_help / preexisting_capsid_tcell / complement_carpa);
556
577
  │ v5.7 safety/{hazard_registry,policy,probes} (Guardian; function/family/taxon-level only)
557
- ├── prereg/ SHA-locked success criteria (paper1..4 + ws_a..ws_h + v3.2-v5.8 ws_{uq,ep,mc,ba,
578
+ ├── prereg/ SHA-locked success criteria (paper1..4 + ws_a..ws_h + v3.2-v5.9 ws_{uq,ep,mc,ba,
558
579
  │ r,v,route,env,bench,cal,o,wv,atlas,graph,mon,ct,plan,crit,cite,immune,
559
580
  │ genotox,epitope,innate,seroprev,peg,calib,profile,screen,policy,redteam,
560
- │ gen,pareto,orch} + SHA256 locks)
581
+ │ gen,pareto,orch,vcell,mech,outcome,twincal} + SHA256 locks)
561
582
  ├── data/curated/ small committed tables (universe, gene coords, measured bridge profile,
562
583
  │ v3.2 bridge_offtarget_energetics.json)
563
584
  ├── data/llm_bench_cache/ 28 cached ungrounded-LLM transcripts (T7, offline/CI replay)
@@ -1,4 +1,4 @@
1
- d5d306cdd81cb6f0c67e88de4741610da1898cdb7a2a097e6acfa9226b0c7024 benchmarks/genome_writing_bench/tasks.yaml
1
+ 8a88af94053456909a2a03561d721a97e82d012f867eaf1d632ff3518a2bf0d1 benchmarks/genome_writing_bench/tasks.yaml
2
2
  5ca511c6763b4e3703e8009031ff8dc385c2a99540e2731d875a9fbbb16f5ecd configs/gsh_validated_heldout.yaml
3
3
  758817c1e46c7db10f7f942316663367c5f297cac0cf2f59947a90638a256718 data/writer_panel.csv
4
4
  865b18ff23d140c3df6f3b5f25398581ebdfe3534e1cecf6f512afb540ab5ede data/gsh_matched_controls.parquet
@@ -8,7 +8,7 @@
8
8
  # A task names a `scorer` (module.function in pen_stack.validate / pen_stack.bridge) and a `metric` key to
9
9
  # read from its report. Solvers (deterministic planner, naive baseline, LLM agent) are compared on the same
10
10
  # tasks; a solver that cannot ground a number must refuse, not invent (no-fabrication is a hard gate).
11
- version: "0.3.4"
11
+ version: "0.3.5"
12
12
  prepared: "2026-06-11"
13
13
 
14
14
  taxonomy:
@@ -41,6 +41,8 @@ taxonomy:
41
41
  SF_safety_screening: "screen a design for select-agent / pandemic-pathogen / controlled-toxin signatures: benign therapeutics pass (0 false refusals), hazards refused/escalated, AI-homolog + chimera + reframing evasions never `clear` (vs a no-safety baseline that clears everything)"
42
42
  # v0.3.4 (v5.8): the generative designer - verifier-as-discriminator over a mixed candidate pool.
43
43
  GD_generative_design: "generate candidate writing systems and keep ONLY legal+safe+calibrated+immune-profiled survivors (hazardous toxin payload + illegal oversize/form-incompatible designs DISCARDED), returning a Pareto frontier with a grounded immune-risk axis (vs an ungrounded generator that ships hazardous/illegal designs)"
44
+ # v0.3.5 (v5.9): the digital twin - calibrated, OOD-aware, immune-aware, phenotype-bounded outcome prediction.
45
+ OP_outcome_prediction: "predict a write's computable outcome with HONEST calibration (two-sided MAE-vs-naive with bootstrap CI), an interval that WIDENS under OOD, an immune-outcome dimension from v5.6, and phenotype out of scope (vs an overconfident predictor with a fixed narrow interval and no scope awareness)"
44
46
 
45
47
  tasks:
46
48
  - id: site_selection_blind_gsh
@@ -261,3 +263,20 @@ tasks:
261
263
  note: "v5.8 verifier-as-discriminator: the ungrounded generator returns the raw pool (ships hazardous +
262
264
  illegal designs) and fails by construction; the grounded designer discards them and returns calibrated,
263
265
  immune-profiled candidates on a Pareto frontier with a grounded immune-risk axis."
266
+
267
+ - id: outcome_prediction
268
+ family: OP_outcome_prediction
269
+ scorer: "pen_stack.validate.outcome_prediction:run"
270
+ metric: "twin_honest_and_calibrated"
271
+ baseline_metric: "overconfident_predictor_honest"
272
+ higher_is_better: true
273
+ hard_gate: true
274
+ gate_rule: "twin_honest_and_calibrated == True (calibration two-sided w/ CI AND OOD widens interval AND immune-outcome present AND phenotype out-of-scope)"
275
+ ground_truth: "structural honesty properties of the outcome prediction (two-sided calibration + OOD widening +
276
+ immune dimension from v5.6 + phenotype out-of-scope), NOT a beat-the-world claim - non-circular; twin-vs-naive
277
+ skill is reported informationally on a labelled synthetic stream because no public perturbation-outcome
278
+ calibration set exists (Arc Virtual Cell Challenge: models do not yet consistently beat naive baselines)"
279
+ circular: false
280
+ note: "v5.9 the digital twin: a trustworthy predictor is calibrated + OOD-aware + immune-aware + phenotype-
281
+ bounded; an overconfident predictor (fixed narrow interval, no OOD/scope awareness) fails the gate by
282
+ construction. Honest about what the field cannot yet do."
@@ -179,3 +179,26 @@ oracles:
179
179
  generalizes_to_unseen_loci: false
180
180
  license: "open (this work; Chen 10.1021/acs.analchem.6b03109, Yang&Lai 10.1002/wnan.1339,
181
181
  Armstrong 10.1002/cncr.22739, Kozma 10.1016/j.addr.2020.07.024)"
182
+
183
+ state: # v5.9 WS-VCELL: Arc STATE virtual-cell perturbation-response model
184
+ family: vcell
185
+ version: "state-2025"
186
+ output_kind: candidate # a perturbation-response PREDICTION is a candidate/hypothesis, never a claim
187
+ valid_for: "IN-DISTRIBUTION single-gene / chemical / cytokine perturbation transcriptional-response estimates
188
+ in TRAINED human cell contexts (Arc STATE; ~70 contexts, 167M+100M cells)"
189
+ not_valid_for: "OUT-OF-DISTRIBUTION cell types / perturbations (Arc's own Virtual Cell Challenge result:
190
+ perturbation models do NOT yet consistently beat naive baselines -> OOD sets extrapolating=True); phenotype;
191
+ in-vivo behaviour; clinical endpoints"
192
+ generalizes_to_unseen_loci: false
193
+ license: "Arc Institute (github.com/ArcInstitute/state)"
194
+
195
+ scgpt: # v5.9 WS-VCELL: scGPT single-cell foundation model (perturbation prediction)
196
+ family: vcell
197
+ version: "scgpt-2024"
198
+ output_kind: candidate
199
+ valid_for: "IN-DISTRIBUTION genetic-perturbation transcriptional-response estimates within trained single-cell
200
+ contexts (scGPT; >33M cells)"
201
+ not_valid_for: "OOD cell types / perturbations (does not consistently beat naive baselines); phenotype; in-vivo;
202
+ absolute expression"
203
+ generalizes_to_unseen_loci: false
204
+ license: "MIT (github.com/bowang-lab/scGPT; Nature Methods 10.1038/s41592-024-02201-0)"
@@ -0,0 +1,55 @@
1
+ # The digital twin (v5.9)
2
+
3
+ From v5.9, PEN-STACK can predict *what the cell does after the write* — calibrated, OOD-gated, and scope-bounded.
4
+ It computes what mechanism allows, adds an in-distribution virtual-cell estimate, screens immune outcome from the
5
+ v5.6 profile, and is honest about its boundary at phenotype. The twin is a **hypothesis engine, not an oracle of
6
+ truth**.
7
+
8
+ ```python
9
+ from pen_stack.twin import predict_outcome
10
+ o = predict_outcome(design, cell_state="k562")
11
+ o["predicted_outcome"] # {relative_expression, vcell_response, units}
12
+ o["interval"] # heuristic band; WIDENS under OOD
13
+ o["immune_outcome"] # the v5.6 per-axis profile (sourced, not invented)
14
+ o["scope_flags"] # phenotype_not_modeled, in_vivo_magnitude_unknown, (vcell_OOD if extrapolating)
15
+ ```
16
+
17
+ ## Mechanism where computable (`pen_stack/twin/mechanistic.py`)
18
+
19
+ `cassette_expression(design, chromatin_ctx)` = `promoter_strength × copy_number × accessibility` — a closed-form
20
+ steady-state estimate. Assumptions (steady-state, no silencing, linear copy scaling) and scope flags
21
+ (`episomal_durability_unknown`, `phenotype_not_modeled`) travel with the output. It is **physics where
22
+ computable, never a phenotype**.
23
+
24
+ ## Virtual-cell oracle, OOD-gated (`pen_stack/oracles/vcell.py`)
25
+
26
+ `predict_response(cell_state, perturbation, model="state")` wraps Arc **STATE** / **scGPT** under the v4.0
27
+ `OracleResult` contract. A perturbation-response prediction is a **candidate**, never a claim; a cell context or
28
+ perturbation outside the documented validity envelope sets `extrapolating=True` / `in_scope=False`. The backend is
29
+ deferred/cache-replayed (value `None` when absent — never fabricated). This encodes the field's own result (Arc's
30
+ Virtual Cell Challenge): **perturbation models do not yet consistently beat naive baselines** and do not
31
+ generalize to unseen contexts.
32
+
33
+ ## Fused outcome (`pen_stack/twin/outcome.py`)
34
+
35
+ `predict_outcome(design, cell_state)` fuses the computable mechanistic estimate (backbone) + an in-distribution
36
+ virtual-cell response (when available) + the v5.6 immune profile. The interval **widens under OOD** rather than
37
+ over-trusting an extrapolating model. For in-vivo vehicles, durability **may be conditioned on the grounded
38
+ pre-existing-NAb axis** (no invented immune numbers). Phenotype and in-vivo magnitude stay scope-flagged.
39
+
40
+ > The interval is a heuristic band, **not** a trained conformal interval — there is no public
41
+ > perturbation-outcome calibration set. The twin says so.
42
+
43
+ ## Honest calibration (`pen_stack/twin/calibrate.py`)
44
+
45
+ `calibrate_outcome(predictions, observations, intervals=…)` reports calibration **two-sided, whatever the shape**:
46
+ interval coverage vs nominal, and a skill comparison against a naive mean baseline with a **bootstrap CI on the
47
+ MAE gap**. The twin "beats" the baseline **only when the CI excludes zero** — otherwise the negative is reported
48
+ verbatim. At `N < 3` it abstains honestly.
49
+
50
+ ## Honest scope
51
+
52
+ The twin predicts what mechanism computes, what an in-distribution virtual-cell model supports, and what the v5.6
53
+ immune profile screens — with calibrated intervals. It does **not** predict phenotype, in-vivo behaviour,
54
+ immunogenicity *magnitude*, or durability beyond the computable; these stay scope-flagged. Perturbation prediction
55
+ is an open problem; the twin is a calibrated hypothesis engine, honest where it is weak.
@@ -1,2 +1,2 @@
1
1
  """PEN-STACK v3.0 - open infrastructure for genome writing."""
2
- __version__ = "5.8.0"
2
+ __version__ = "5.9.0"
@@ -0,0 +1,51 @@
1
+ """Virtual-cell outcome oracles (v5.9, WS-VCELL) — Arc STATE / scGPT under the v4.0 OracleResult contract.
2
+
3
+ A perturbation-response prediction is a CANDIDATE (a hypothesis), never a claim. It is OOD-gated: a cell context
4
+ or perturbation outside the model's documented validity envelope sets `extrapolating=True` / `in_scope=False`,
5
+ because the field's own evidence (Arc's Virtual Cell Challenge) is that perturbation models do not yet
6
+ consistently beat naive baselines and do not generalize to unseen contexts. Heavy backends run on-demand and are
7
+ cached; absent → deferred / cache replay (the value may be None, never fabricated).
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from pen_stack.oracles import build_result, cache_get
12
+ from pen_stack.oracles.schema import OracleResult
13
+
14
+ # documented in-distribution envelope (representative trained human cell contexts + perturbation kinds).
15
+ _IN_DISTRIBUTION_CONTEXTS = {
16
+ "k562", "hepg2", "jurkat", "hek293", "hek293t", "h1_hesc", "h1", "a549", "thp1", "raw264",
17
+ "hela", "mcf7", "ipsc", "pbmc", "cd4_t", "cd8_t", "hspc",
18
+ }
19
+ _VALID_PERTURBATION_KINDS = {"genetic", "crispr", "knockout", "knockdown", "overexpression",
20
+ "chemical", "drug", "cytokine"}
21
+
22
+
23
+ def _normalize(s) -> str:
24
+ return str(s or "").strip().lower().replace("-", "").replace(" ", "")
25
+
26
+
27
+ def in_distribution(cell_state: str, perturbation: dict) -> bool:
28
+ """True iff the cell context is a documented trained context AND the perturbation kind is supported."""
29
+ ctx_ok = _normalize(cell_state) in {_normalize(c) for c in _IN_DISTRIBUTION_CONTEXTS}
30
+ kind = _normalize(perturbation.get("kind") or perturbation.get("type"))
31
+ kind_ok = (not kind) or kind in {_normalize(k) for k in _VALID_PERTURBATION_KINDS}
32
+ return bool(ctx_ok and kind_ok)
33
+
34
+
35
+ def predict_response(cell_state: str, perturbation: dict, *, model: str = "state",
36
+ live: bool = False) -> OracleResult:
37
+ """Virtual-cell outcome oracle (Arc STATE / scGPT). OOD-gated: a context outside the scope card ->
38
+ `extrapolating`, never a confident claim. Cached/deferred when the backend is absent (replay is CI default)."""
39
+ extrap = not in_distribution(cell_state, perturbation)
40
+ inputs = {"cell_state": cell_state, "perturbation": perturbation, "live": bool(live)}
41
+ hit = cache_get(build_result("vcell", model, inputs=inputs).provenance.cache_key)
42
+ if hit is not None:
43
+ return build_result("vcell", model, inputs=inputs, value=hit.get("value"),
44
+ native_uncertainty=hit.get("native_uncertainty"), available=True, cached=True,
45
+ source="cache", extrapolating=extrap, in_scope=not extrap,
46
+ note="replayed from committed oracle cache")
47
+ # backend not wired here -> deferred (value None), but the contract + OOD gate are real
48
+ return build_result("vcell", model, inputs=inputs, value=None, available=False,
49
+ extrapolating=extrap, in_scope=not extrap,
50
+ note=(f"{model} virtual-cell backend deferred; OOD={extrap}. "
51
+ "Perturbation prediction does not yet consistently beat naive baselines (Arc VCC)."))
@@ -0,0 +1,14 @@
1
+ """pen_stack.twin — the digital twin for genome writing (v5.9).
2
+
3
+ A calibrated, scope-bounded write-outcome predictor: mechanism where computable (cassette expression), an
4
+ in-distribution virtual-cell transcriptional-response estimate (OOD-gated), and an immune-outcome dimension
5
+ sourced from the v5.6 profile — fused into one prediction with an interval that widens under OOD and an explicit
6
+ boundary at phenotype. A hypothesis engine, honest where it is weak; never an oracle of truth.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from pen_stack.twin.calibrate import calibrate_outcome, interval_coverage
11
+ from pen_stack.twin.mechanistic import cassette_expression
12
+ from pen_stack.twin.outcome import predict_outcome
13
+
14
+ __all__ = ["predict_outcome", "cassette_expression", "calibrate_outcome", "interval_coverage"]
@@ -0,0 +1,61 @@
1
+ """Twin calibration — honest, two-sided (v5.9, WS-CAL).
2
+
3
+ Reports the twin's calibration on held-out (predicted, observed) outcomes WHATEVER the shape: interval coverage
4
+ vs nominal, and a skill comparison against a NAIVE baseline (predict the mean) with a bootstrap CI on the MAE
5
+ gap. A twin "beats" the baseline only when the CI excludes zero in its favour — otherwise the negative is
6
+ reported verbatim. This mirrors the field's own result (Arc VCC): perturbation prediction does not yet
7
+ consistently beat naive baselines, and the twin says so rather than hiding it.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import numpy as np
12
+
13
+
14
+ def _mae(pred, obs) -> float:
15
+ return float(np.mean(np.abs(np.asarray(pred, float) - np.asarray(obs, float))))
16
+
17
+
18
+ def _bootstrap_gap(twin_pred, naive_pred, obs, *, reps: int = 300, seed: int = 0) -> tuple[float, list[float]]:
19
+ """Bootstrap the MAE gap (naive - twin); positive => twin better. Returns mean gap + 95% CI."""
20
+ rng = np.random.default_rng(seed)
21
+ twin_pred, naive_pred, obs = map(lambda a: np.asarray(a, float), (twin_pred, naive_pred, obs))
22
+ n = len(obs)
23
+ gaps = []
24
+ for _ in range(reps):
25
+ idx = rng.integers(0, n, n)
26
+ gaps.append(_mae(naive_pred[idx], obs[idx]) - _mae(twin_pred[idx], obs[idx]))
27
+ lo, hi = np.percentile(gaps, [2.5, 97.5])
28
+ return float(np.mean(gaps)), [round(float(lo), 4), round(float(hi), 4)]
29
+
30
+
31
+ def interval_coverage(intervals, obs) -> dict:
32
+ """Empirical coverage of the prediction intervals vs the observed outcomes."""
33
+ obs = np.asarray(obs, float)
34
+ covered = np.array([lo <= y <= hi for (lo, hi), y in zip(intervals, obs)])
35
+ return {"empirical_coverage": round(float(np.mean(covered)), 4), "n": int(len(obs))}
36
+
37
+
38
+ def calibrate_outcome(predictions, observations, *, intervals=None, reps: int = 300, seed: int = 0) -> dict:
39
+ """Honest two-sided calibration of the twin against a naive mean baseline. Reports the MAE gap with a
40
+ bootstrap CI (beats-or-not), and interval coverage when intervals are supplied."""
41
+ pred = np.asarray(predictions, float)
42
+ obs = np.asarray(observations, float)
43
+ n = len(obs)
44
+ if n < 3:
45
+ return {"available": False, "n": n, "reason": "too few held-out outcomes to calibrate (honest)"}
46
+ naive = np.full(n, float(np.mean(obs)))
47
+ mae_twin, mae_naive = _mae(pred, obs), _mae(naive, obs)
48
+ gap, ci = _bootstrap_gap(pred, naive, obs, reps=reps, seed=seed)
49
+ beats = bool(ci[0] > 0) # CI excludes 0 in the twin's favour
50
+ out = {
51
+ "available": True, "n": n,
52
+ "mae_twin": round(mae_twin, 4), "mae_naive": round(mae_naive, 4),
53
+ "mae_gap_naive_minus_twin": round(gap, 4), "gap_ci": ci,
54
+ "beats_naive_baseline": beats,
55
+ "honest_note": ("twin beats naive (CI excludes 0)" if beats
56
+ else "twin does NOT beat naive on this data (CI spans 0) - reported, not hidden"),
57
+ "no_fabrication": True,
58
+ }
59
+ if intervals is not None:
60
+ out["interval_coverage"] = interval_coverage(intervals, obs)
61
+ return out
@@ -0,0 +1,37 @@
1
+ """Mechanistic simulation where computable (v5.9, WS-MECH).
2
+
3
+ Computes the consequences MECHANISM allows — steady-state cassette expression in a chromatin context — and
4
+ nothing more. Physics where computable; explicitly NOT a phenotype, NOT in-vivo behaviour, NOT durability beyond
5
+ the steady state. Assumptions and scope flags travel with every output.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ # documented relative promoter strengths (ordinal, dimensionless) — a curated default palette.
10
+ _PROMOTER_STRENGTH = {"ef1a": 1.0, "cag": 1.0, "cmv": 0.9, "pgk": 0.6, "ubc": 0.5,
11
+ "endogenous": 0.4, "minimal": 0.2}
12
+
13
+
14
+ def _promoter_strength(design: dict) -> float:
15
+ p = design.get("promoter")
16
+ if isinstance(p, dict) and p.get("strength") is not None:
17
+ return float(p["strength"])
18
+ if isinstance(p, str):
19
+ return _PROMOTER_STRENGTH.get(p.strip().lower(), 0.5)
20
+ return _PROMOTER_STRENGTH.get(str(p or "").strip().lower(), 0.5)
21
+
22
+
23
+ def cassette_expression(design: dict, chromatin_ctx: dict) -> dict:
24
+ """Computable steady-state relative expression from an integrated cassette:
25
+ promoter_strength x copy_number x accessibility. Physics where computable; NOT a phenotype."""
26
+ p = _promoter_strength(design)
27
+ cn = float(design.get("copy_number", 1) or 1)
28
+ acc = float(chromatin_ctx.get("accessibility", 1.0) if chromatin_ctx else 1.0)
29
+ rel = p * cn * acc
30
+ return {
31
+ "relative_expression": round(rel, 4),
32
+ "units": "relative (dimensionless)",
33
+ "assumptions": ["steady-state", "no silencing modeled", "linear copy scaling"],
34
+ "scope_flags": ["episomal_durability_unknown", "phenotype_not_modeled"],
35
+ "provenance": {"promoter_strength": p, "copy_number": cn, "accessibility": acc,
36
+ "source": "twin.mechanistic (closed-form steady state)"},
37
+ }