pen-stack 4.5.1__tar.gz → 5.0.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 (302) hide show
  1. {pen_stack-4.5.1 → pen_stack-5.0.0}/CHANGELOG.md +25 -0
  2. {pen_stack-4.5.1 → pen_stack-5.0.0}/CITATION.cff +1 -1
  3. {pen_stack-4.5.1 → pen_stack-5.0.0}/PKG-INFO +22 -5
  4. {pen_stack-4.5.1 → pen_stack-5.0.0}/README.md +21 -4
  5. {pen_stack-4.5.1 → pen_stack-5.0.0}/benchmarks/genome_writing_bench/LEADERBOARD.md +6 -5
  6. {pen_stack-4.5.1 → pen_stack-5.0.0}/benchmarks/genome_writing_bench/tasks.yaml +18 -1
  7. pen_stack-5.0.0/docs/co_scientist.md +31 -0
  8. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/__init__.py +1 -1
  9. pen_stack-5.0.0/pen_stack/agent/cite.py +118 -0
  10. pen_stack-5.0.0/pen_stack/agent/co_scientist.py +232 -0
  11. pen_stack-5.0.0/pen_stack/validate/bench_coscientist_tasks.py +60 -0
  12. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack.egg-info/PKG-INFO +22 -5
  13. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack.egg-info/SOURCES.txt +10 -0
  14. pen_stack-5.0.0/prereg/SHA256_LOCK_ws_cite.json +8 -0
  15. pen_stack-5.0.0/prereg/SHA256_LOCK_ws_crit.json +8 -0
  16. pen_stack-5.0.0/prereg/SHA256_LOCK_ws_plan.json +8 -0
  17. pen_stack-5.0.0/prereg/ws_cite.yaml +17 -0
  18. pen_stack-5.0.0/prereg/ws_crit.yaml +16 -0
  19. pen_stack-5.0.0/prereg/ws_plan.yaml +18 -0
  20. {pen_stack-4.5.1 → pen_stack-5.0.0}/pyproject.toml +1 -1
  21. {pen_stack-4.5.1 → pen_stack-5.0.0}/LICENSE +0 -0
  22. {pen_stack-4.5.1 → pen_stack-5.0.0}/MANIFEST.in +0 -0
  23. {pen_stack-4.5.1 → pen_stack-5.0.0}/bench/run.py +0 -0
  24. {pen_stack-4.5.1 → pen_stack-5.0.0}/benchmarks/genome_writing_bench/README.md +0 -0
  25. {pen_stack-4.5.1 → pen_stack-5.0.0}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
  26. {pen_stack-4.5.1 → pen_stack-5.0.0}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  27. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/atlas_families.yaml +0 -0
  28. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/bridge_offtarget_profile.yaml +0 -0
  29. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/cargo_polish.yaml +0 -0
  30. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/cell_types.yaml +0 -0
  31. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/datasets.yaml +0 -0
  32. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/delivery_constraints.yaml +0 -0
  33. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/delivery_rules.yaml +0 -0
  34. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/delivery_vehicles.yaml +0 -0
  35. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/gates_v3.yaml +0 -0
  36. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/gsh_validated_heldout.yaml +0 -0
  37. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/intent_weights.yaml +0 -0
  38. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/known_unknowns.yaml +0 -0
  39. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/llm.yaml +0 -0
  40. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/monitor_queries.yaml +0 -0
  41. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/oracles/scope_cards.yaml +0 -0
  42. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/rules/delivery.yaml +0 -0
  43. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/rules/fold.yaml +0 -0
  44. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/rules/multiplex.yaml +0 -0
  45. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/rules/payload.yaml +0 -0
  46. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/rules/reachability.yaml +0 -0
  47. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/score_axes.yaml +0 -0
  48. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/target_sites.yaml +0 -0
  49. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/universe_crosswalk.yaml +0 -0
  50. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/write_types.yaml +0 -0
  51. {pen_stack-4.5.1 → pen_stack-5.0.0}/configs/wtkb_curated.yaml +0 -0
  52. {pen_stack-4.5.1 → pen_stack-5.0.0}/data/curated/bridge_offtarget_energetics.json +0 -0
  53. {pen_stack-4.5.1 → pen_stack-5.0.0}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  54. {pen_stack-4.5.1 → pen_stack-5.0.0}/data/curated/gene_coords.parquet +0 -0
  55. {pen_stack-4.5.1 → pen_stack-5.0.0}/data/curated/unified_editor_universe.parquet +0 -0
  56. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/BACKLOG.md +0 -0
  57. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/DEPLOY.md +0 -0
  58. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/INFRA.md +0 -0
  59. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/MCP.md +0 -0
  60. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/RELEASING.md +0 -0
  61. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/REPRO.md +0 -0
  62. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/agent.md +0 -0
  63. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/alphagenome_feasibility.md +0 -0
  64. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/benchmark_circularity.md +0 -0
  65. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/cards/atlas.md +0 -0
  66. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/cards/durability.md +0 -0
  67. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/cards/safety.md +0 -0
  68. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/delivery.md +0 -0
  69. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/dissemination.md +0 -0
  70. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/environment.md +0 -0
  71. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/index.md +0 -0
  72. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/mechanistic_constraints.md +0 -0
  73. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/oracles.md +0 -0
  74. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/positioning.md +0 -0
  75. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/private_data_formats.md +0 -0
  76. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/quickstart.md +0 -0
  77. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/rules.md +0 -0
  78. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/scope.md +0 -0
  79. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/scorecard.md +0 -0
  80. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/tutorials/compare-families.md +0 -0
  81. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/tutorials/score-deliverability.md +0 -0
  82. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/tutorials/where-can-i-write.md +0 -0
  83. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  84. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/uncertainty.md +0 -0
  85. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/verify.md +0 -0
  86. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/world_model.md +0 -0
  87. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/writer_verification.md +0 -0
  88. {pen_stack-4.5.1 → pen_stack-5.0.0}/docs/wtkb.md +0 -0
  89. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/_resources.py +0 -0
  90. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/adapt/__init__.py +0 -0
  91. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/adapt/finetune.py +0 -0
  92. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/adapt/ingest.py +0 -0
  93. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/adapt/pipeline.py +0 -0
  94. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/adapt/recalibrate.py +0 -0
  95. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/adapt/report.py +0 -0
  96. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/__init__.py +0 -0
  97. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/epistemic.py +0 -0
  98. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/guardrails.py +0 -0
  99. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/mcp_server.py +0 -0
  100. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/orchestrator.py +0 -0
  101. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/pen_agent.py +0 -0
  102. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/scope.py +0 -0
  103. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/agent/tools.py +0 -0
  104. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/__init__.py +0 -0
  105. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/build_wtkb.py +0 -0
  106. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/crosslink.py +0 -0
  107. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/expand.py +0 -0
  108. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/schema.py +0 -0
  109. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/scorecard.py +0 -0
  110. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/universe.py +0 -0
  111. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/variant_propose.py +0 -0
  112. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/atlas/writer_verify.py +0 -0
  113. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/__init__.py +0 -0
  114. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/activity.py +0 -0
  115. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/cli.py +0 -0
  116. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/fold_qc.py +0 -0
  117. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/guide_qc.py +0 -0
  118. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/ingest.py +0 -0
  119. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/offtarget.py +0 -0
  120. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/offtarget_energetics.py +0 -0
  121. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/ortholog_screen.py +0 -0
  122. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/bridge/pipeline.py +0 -0
  123. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/cli.py +0 -0
  124. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/data/__init__.py +0 -0
  125. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/data/encode.py +0 -0
  126. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/data/genome.py +0 -0
  127. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/data/ingest_chromatin.py +0 -0
  128. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/data/ingest_integration.py +0 -0
  129. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/data/ingest_safety_annot.py +0 -0
  130. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/data/ingest_trip.py +0 -0
  131. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/env/__init__.py +0 -0
  132. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/env/genome_writing_env.py +0 -0
  133. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/env/policies.py +0 -0
  134. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/graph/__init__.py +0 -0
  135. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/graph/build.py +0 -0
  136. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/graph/cell_types.py +0 -0
  137. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/graph/ingest.py +0 -0
  138. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/graph/query.py +0 -0
  139. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/graph/schema.py +0 -0
  140. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/mech/__init__.py +0 -0
  141. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/mech/classify_atlas.py +0 -0
  142. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/mech/whitelist.py +0 -0
  143. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/monitor/__init__.py +0 -0
  144. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/monitor/europepmc.py +0 -0
  145. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/monitor/run.py +0 -0
  146. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/monitor/triage.py +0 -0
  147. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/__init__.py +0 -0
  148. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/cache.py +0 -0
  149. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/energetics.py +0 -0
  150. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/genome.py +0 -0
  151. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/protein_design.py +0 -0
  152. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/rna.py +0 -0
  153. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/schema.py +0 -0
  154. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/oracles/structure.py +0 -0
  155. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/__init__.py +0 -0
  156. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/cargo.py +0 -0
  157. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/cargo_polish.py +0 -0
  158. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/delivery.py +0 -0
  159. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/delivery_constraints.py +0 -0
  160. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/delivery_vehicles.py +0 -0
  161. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/multiplex.py +0 -0
  162. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/optimize.py +0 -0
  163. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/pipeline.py +0 -0
  164. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/report.py +0 -0
  165. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/router.py +0 -0
  166. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/planner/target_site.py +0 -0
  167. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rag/__init__.py +0 -0
  168. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rag/index.py +0 -0
  169. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rag/llm.py +0 -0
  170. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rag/qa.py +0 -0
  171. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rules/__init__.py +0 -0
  172. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rules/evaluators.py +0 -0
  173. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rules/loader.py +0 -0
  174. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rules/schema.py +0 -0
  175. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/rules/solver.py +0 -0
  176. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/score/__init__.py +0 -0
  177. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/score/recalibrate.py +0 -0
  178. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/score/therapeutic.py +0 -0
  179. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/server/__init__.py +0 -0
  180. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/server/api.py +0 -0
  181. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/ui/__init__.py +0 -0
  182. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/ui/app.py +0 -0
  183. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/__init__.py +0 -0
  184. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/adapt_demo.py +0 -0
  185. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/agent_eval.py +0 -0
  186. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  187. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/bench_graph_tasks.py +0 -0
  188. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/bench_rule_tasks.py +0 -0
  189. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/bench_trust_tasks.py +0 -0
  190. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  191. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  192. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/cargo_directionality.py +0 -0
  193. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/durability_baselines.py +0 -0
  194. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/forward_hypotheses.py +0 -0
  195. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/guide_qc_demo.py +0 -0
  196. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/intent_specification.py +0 -0
  197. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  198. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  199. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/outcome_calibration.py +0 -0
  200. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/paper3_benchmark.py +0 -0
  201. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/paper4_real_validation.py +0 -0
  202. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/paper4_validation.py +0 -0
  203. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/selective_prediction.py +0 -0
  204. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/seq_vs_measured.py +0 -0
  205. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/target_site_controls.py +0 -0
  206. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/uncertainty_eval.py +0 -0
  207. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/ungrounded_baseline.py +0 -0
  208. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/within_locus_ranking.py +0 -0
  209. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/validate/writer_recovery.py +0 -0
  210. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/verify/__init__.py +0 -0
  211. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/verify/schema.py +0 -0
  212. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/verify/service.py +0 -0
  213. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/__init__.py +0 -0
  214. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/chromatin_seq.py +0 -0
  215. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/durability.py +0 -0
  216. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/export_tracks.py +0 -0
  217. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/features.py +0 -0
  218. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/gsh_baseline.py +0 -0
  219. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/mesh_features.py +0 -0
  220. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/ood.py +0 -0
  221. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/providers.py +0 -0
  222. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/safety.py +0 -0
  223. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/structure3d.py +0 -0
  224. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/uncertainty.py +0 -0
  225. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack/wgenome/writability.py +0 -0
  226. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack.egg-info/dependency_links.txt +0 -0
  227. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack.egg-info/entry_points.txt +0 -0
  228. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack.egg-info/requires.txt +0 -0
  229. {pen_stack-4.5.1 → pen_stack-5.0.0}/pen_stack.egg-info/top_level.txt +0 -0
  230. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_phase0.json +0 -0
  231. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  232. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_phase2.json +0 -0
  233. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_phase3.json +0 -0
  234. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_a.json +0 -0
  235. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  236. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_b.json +0 -0
  237. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  238. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  239. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  240. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  241. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_c.json +0 -0
  242. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  243. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  244. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_d.json +0 -0
  245. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_e.json +0 -0
  246. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_env.json +0 -0
  247. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  248. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_f.json +0 -0
  249. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_g.json +0 -0
  250. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  251. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_h.json +0 -0
  252. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  253. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  254. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_o.json +0 -0
  255. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_r.json +0 -0
  256. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_route.json +0 -0
  257. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  258. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_v.json +0 -0
  259. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  260. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/paper1.yaml +0 -0
  261. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/paper2.yaml +0 -0
  262. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/paper3.yaml +0 -0
  263. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/paper4.yaml +0 -0
  264. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/phase0.yaml +0 -0
  265. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_a.yaml +0 -0
  266. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_atlas.yaml +0 -0
  267. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_b.yaml +0 -0
  268. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_ba.yaml +0 -0
  269. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_ba_v33.yaml +0 -0
  270. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_ba_v45.yaml +0 -0
  271. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_bench.yaml +0 -0
  272. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_c.yaml +0 -0
  273. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_cal.yaml +0 -0
  274. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_ct.yaml +0 -0
  275. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_d.yaml +0 -0
  276. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_e.yaml +0 -0
  277. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_env.yaml +0 -0
  278. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_ep.yaml +0 -0
  279. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_f.yaml +0 -0
  280. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_g.yaml +0 -0
  281. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_graph.yaml +0 -0
  282. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_h.yaml +0 -0
  283. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_mc.yaml +0 -0
  284. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_mon.yaml +0 -0
  285. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_o.yaml +0 -0
  286. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_r.yaml +0 -0
  287. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_route.yaml +0 -0
  288. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_uq.yaml +0 -0
  289. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_v.yaml +0 -0
  290. {pen_stack-4.5.1 → pen_stack-5.0.0}/prereg/ws_wv.yaml +0 -0
  291. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p1_build_atlas.py +0 -0
  292. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p1_build_durability.py +0 -0
  293. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p1_export_tracks.py +0 -0
  294. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p1_safety_concordance.py +0 -0
  295. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p1_train_safety.py +0 -0
  296. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p1_validation_report.py +0 -0
  297. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p2_build_atlas.py +0 -0
  298. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p3_benchmark_report.py +0 -0
  299. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/p4_genome_scan.py +0 -0
  300. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/ws_b_report.py +0 -0
  301. {pen_stack-4.5.1 → pen_stack-5.0.0}/scripts/ws_c_report.py +0 -0
  302. {pen_stack-4.5.1 → pen_stack-5.0.0}/setup.cfg +0 -0
@@ -3,6 +3,31 @@
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.0.0] - 2026-06-09 - v5.0 release: the Co-Scientist (capstone — smart because it is grounded)
7
+
8
+ The reasoning ceiling rises while the grounding floor stays fixed: a co-scientist that proposes multiple
9
+ distinct strategies, critiques and revises its own plans, cites its reasoning, and itemises what it cannot
10
+ assess — with **no-fabrication holding across the full reasoning stack** (the central gate). Workstreams
11
+ WS-{PLAN,MULTI,CRIT,SCOPE2,CITE,GEN}, each SHA-locked.
12
+
13
+ ### Added
14
+ - **WS-PLAN + WS-MULTI** — `pen_stack/agent/co_scientist.py`: `propose_strategies()` returns 2–3 **materially
15
+ distinct** strategies (≥2 design axes differ — measured by `distinctness()`, not reworded), each
16
+ independently legal + confidence-tagged; `deliberate()` benchmarks the deliberative planner vs the
17
+ deterministic baseline. `prereg/ws_plan.yaml`.
18
+ - **WS-CRIT + WS-SCOPE2** — `critique()` / `critique_and_revise()` (the critic only flags + swaps a design
19
+ choice, never invents a number; revisions re-verified) + `critique_falsifiability()` (improves flawed plans
20
+ illegal→legal, 0 spurious revisions on clean) + `scope_ledger()` (per-recommendation: what was/ wasn't
21
+ assessed, the known-unknowns itemised). `prereg/ws_crit.yaml`.
22
+ - **WS-CITE + WS-GEN** — `pen_stack/agent/cite.py`: `cited_rationale()` (citations drawn from the curated
23
+ world-model → resolve by construction) + `citations_grounded()` guard (rejects any DOI not in the curated
24
+ set) + `generalise()` (adjacent tasks grounded-or-refused). `prereg/ws_cite.yaml`.
25
+ - **Bench v0.3.2** — `co_scientist_grounded` reference-solver task: grounded rate 1.0 vs ungrounded 0.0;
26
+ no-fabrication across the full stack. `docs/co_scientist.md`.
27
+
28
+ ### Changed
29
+ - Version 4.5.1 -> 5.0.0 (major — the substrate matured into a grounded co-scientist); bench 0.3.1 -> 0.3.2.
30
+
6
31
  ## [4.5.1] - 2026-06-09 - ID-correctness patch: cell-type ontology IDs
7
32
 
8
33
  ### Fixed
@@ -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: 4.5.1
4
+ version: 5.0.0
5
5
  date-released: 2026-06-01
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: 4.5.1
3
+ Version: 5.0.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
@@ -89,12 +89,12 @@ and durably write new DNA, **which enzyme** can write it there, and **how** to d
89
89
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
90
90
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
91
91
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
92
- [![Version](https://img.shields.io/badge/version-4.5.1-blue.svg)](CHANGELOG.md)
93
- [![Tests](https://img.shields.io/badge/tests-224%20passing-success.svg)](tests/)
92
+ [![Version](https://img.shields.io/badge/version-5.0.0-blue.svg)](CHANGELOG.md)
93
+ [![Tests](https://img.shields.io/badge/tests-240%20passing-success.svg)](tests/)
94
94
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
95
95
  [![Runtime: Docker](https://img.shields.io/badge/runtime-docker-2496ED.svg)](docker/)
96
96
  [![Validation: pre-registered](https://img.shields.io/badge/validation-pre--registered-critical.svg)](prereg/)
97
- [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.1-6f42c1.svg)](benchmarks/genome_writing_bench/)
97
+ [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.2-6f42c1.svg)](benchmarks/genome_writing_bench/)
98
98
 
99
99
  **Built on five prior, separately published repositories:**
100
100
 
@@ -133,6 +133,23 @@ Two questions gate every genome-writing project, and before PEN-STACK no resourc
133
133
  Everything is built on bulk-downloadable public data, runs on a single GPU, and is validated **blind** against
134
134
  a pre-registered, honest baseline before release.
135
135
 
136
+ ## What is new in v5.0 — the Co-Scientist (smart because it is grounded)
137
+
138
+ v5.0 matures the reasoning layer on top of everything beneath it. Given a goal and an intent, PEN-STACK
139
+ returns a small set of **materially distinct, ranked, fully-traceable strategies** — each verified,
140
+ calibrated, cited, and scope-ledgered — while the **no-fabrication guarantee holds by construction**: the
141
+ reasoning layer proposes and critiques, but every number still comes from a validated tool or oracle.
142
+ Intelligence rises while groundedness never falls.
143
+
144
+ | Workstream | What it adds | Result |
145
+ |---|---|---|
146
+ | **PLAN + MULTI** | `agent/co_scientist.py` — `propose_strategies()` / `deliberate()` | 2–3 **materially-distinct** strategies (≥2 design axes differ — *measured*, not reworded), each independently **legal** + **confidence-tagged**; deliberative planner benchmarked vs the deterministic baseline |
147
+ | **CRIT + SCOPE2** | self-critique/revise loop + scope ledger | the critic only flags + swaps (never invents a number); revisions are **re-verified** and **falsifiable** (improve flawed plans illegal→legal, never touch clean ones); every recommendation carries a **complete scope ledger** itemising the known-unknowns |
148
+ | **CITE + GEN** | `agent/cite.py` — cited rationale + scoped generalisation | citations are **drawn from the curated world-model** (resolve by construction); a guard **rejects any hallucinated DOI**; adjacent tasks are **grounded-or-refused** |
149
+ | **central gate** | `co_scientist_grounded` bench (v0.3.2) | grounded rate **1.0** vs ungrounded **0.0**; **no-fabrication holds across the full reasoning stack** (asserted) |
150
+
151
+ See `docs/co_scientist.md` and `prereg/ws_{plan,crit,cite}.yaml`.
152
+
136
153
  ## What is new in v4.5 — the Living World-Model (a knowledge graph that keeps itself current)
137
154
 
138
155
  v4.5 promotes the flat atlas/WT-KB/crosslink tables into a queryable **knowledge graph**: writers, loci,
@@ -412,7 +429,7 @@ pen-stack/
412
429
  │ │ + v3.3 router (write-type dispatch) / delivery_vehicles (8-vehicle palette)
413
430
  │ ├── bridge/ bridge off-target engine (Paper 4): offtarget / fold_qc / guide_qc / pipeline / cli
414
431
  │ │ + v3.2 offtarget_energetics (position x substitution; held-out 0.88, ships)
415
- │ ├── agent/ agentic platform: tools / orchestrator / pen_agent / mcp_server / guardrails
432
+ │ ├── agent/ agentic platform: tools / orchestrator / pen_agent / mcp_server / guardrails; v5.0 co_scientist + cite (multi-strategy, self-critique, cited rationale, scope ledger)
416
433
  │ │ + v3.2 epistemic (3-tier status) / scope (known-unknowns matcher)
417
434
  │ ├── graph/ v4.5 living world-model knowledge graph (schema/build/query/ingest/cell_types); typed provenanced edges; gated living loop (propose-only)
418
435
  │ ├── oracles/ v4.0 L1 oracle mesh: OracleResult contract + adapters (genome/structure/protein_design/rna/energetics) over the foundation models; version-pinned cache
@@ -14,12 +14,12 @@ and durably write new DNA, **which enzyme** can write it there, and **how** to d
14
14
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
15
15
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
16
16
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
17
- [![Version](https://img.shields.io/badge/version-4.5.1-blue.svg)](CHANGELOG.md)
18
- [![Tests](https://img.shields.io/badge/tests-224%20passing-success.svg)](tests/)
17
+ [![Version](https://img.shields.io/badge/version-5.0.0-blue.svg)](CHANGELOG.md)
18
+ [![Tests](https://img.shields.io/badge/tests-240%20passing-success.svg)](tests/)
19
19
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
20
20
  [![Runtime: Docker](https://img.shields.io/badge/runtime-docker-2496ED.svg)](docker/)
21
21
  [![Validation: pre-registered](https://img.shields.io/badge/validation-pre--registered-critical.svg)](prereg/)
22
- [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.1-6f42c1.svg)](benchmarks/genome_writing_bench/)
22
+ [![Genome-Writing Bench v0.3](https://img.shields.io/badge/benchmark-Genome--Writing%20Bench%20v0.3.2-6f42c1.svg)](benchmarks/genome_writing_bench/)
23
23
 
24
24
  **Built on five prior, separately published repositories:**
25
25
 
@@ -58,6 +58,23 @@ Two questions gate every genome-writing project, and before PEN-STACK no resourc
58
58
  Everything is built on bulk-downloadable public data, runs on a single GPU, and is validated **blind** against
59
59
  a pre-registered, honest baseline before release.
60
60
 
61
+ ## What is new in v5.0 — the Co-Scientist (smart because it is grounded)
62
+
63
+ v5.0 matures the reasoning layer on top of everything beneath it. Given a goal and an intent, PEN-STACK
64
+ returns a small set of **materially distinct, ranked, fully-traceable strategies** — each verified,
65
+ calibrated, cited, and scope-ledgered — while the **no-fabrication guarantee holds by construction**: the
66
+ reasoning layer proposes and critiques, but every number still comes from a validated tool or oracle.
67
+ Intelligence rises while groundedness never falls.
68
+
69
+ | Workstream | What it adds | Result |
70
+ |---|---|---|
71
+ | **PLAN + MULTI** | `agent/co_scientist.py` — `propose_strategies()` / `deliberate()` | 2–3 **materially-distinct** strategies (≥2 design axes differ — *measured*, not reworded), each independently **legal** + **confidence-tagged**; deliberative planner benchmarked vs the deterministic baseline |
72
+ | **CRIT + SCOPE2** | self-critique/revise loop + scope ledger | the critic only flags + swaps (never invents a number); revisions are **re-verified** and **falsifiable** (improve flawed plans illegal→legal, never touch clean ones); every recommendation carries a **complete scope ledger** itemising the known-unknowns |
73
+ | **CITE + GEN** | `agent/cite.py` — cited rationale + scoped generalisation | citations are **drawn from the curated world-model** (resolve by construction); a guard **rejects any hallucinated DOI**; adjacent tasks are **grounded-or-refused** |
74
+ | **central gate** | `co_scientist_grounded` bench (v0.3.2) | grounded rate **1.0** vs ungrounded **0.0**; **no-fabrication holds across the full reasoning stack** (asserted) |
75
+
76
+ See `docs/co_scientist.md` and `prereg/ws_{plan,crit,cite}.yaml`.
77
+
61
78
  ## What is new in v4.5 — the Living World-Model (a knowledge graph that keeps itself current)
62
79
 
63
80
  v4.5 promotes the flat atlas/WT-KB/crosslink tables into a queryable **knowledge graph**: writers, loci,
@@ -337,7 +354,7 @@ pen-stack/
337
354
  │ │ + v3.3 router (write-type dispatch) / delivery_vehicles (8-vehicle palette)
338
355
  │ ├── bridge/ bridge off-target engine (Paper 4): offtarget / fold_qc / guide_qc / pipeline / cli
339
356
  │ │ + v3.2 offtarget_energetics (position x substitution; held-out 0.88, ships)
340
- │ ├── agent/ agentic platform: tools / orchestrator / pen_agent / mcp_server / guardrails
357
+ │ ├── agent/ agentic platform: tools / orchestrator / pen_agent / mcp_server / guardrails; v5.0 co_scientist + cite (multi-strategy, self-critique, cited rationale, scope ledger)
341
358
  │ │ + v3.2 epistemic (3-tier status) / scope (known-unknowns matcher)
342
359
  │ ├── graph/ v4.5 living world-model knowledge graph (schema/build/query/ingest/cell_types); typed provenanced edges; gated living loop (propose-only)
343
360
  │ ├── oracles/ v4.0 L1 oracle mesh: OracleResult contract + adapters (genome/structure/protein_design/rna/energetics) over the foundation models; version-pinned cache
@@ -1,12 +1,12 @@
1
- # Genome-Writing Bench v0.3.1 - Leaderboard
1
+ # Genome-Writing Bench v0.3.2 - Leaderboard
2
2
 
3
- Tasks: **15/15 available** in this run (unavailable = needs the Phase-1 atlas / Perry tables / an LLM, which run on the VM/local).
4
- Deterministic planner beats the naive baseline on **11/11** grounded tasks with a baseline.
3
+ Tasks: **16/16 available** in this run (unavailable = needs the Phase-1 atlas / Perry tables / an LLM, which run on the VM/local).
4
+ Deterministic planner beats the naive baseline on **12/12** grounded tasks with a baseline.
5
5
 
6
6
  | Solver | Tasks scored | Beats naive | No-fabrication | Note |
7
7
  |---|---|---|---|---|
8
- | deterministic_planner | 15 | 11/11 | n/a (deterministic) | validated planning tools - the reference |
9
- | naive_baseline | 11 | - | n/a (deterministic) | safety-only / prevalence / Hamming baselines |
8
+ | deterministic_planner | 16 | 12/12 | n/a (deterministic) | validated planning tools - the reference |
9
+ | naive_baseline | 12 | - | n/a (deterministic) | safety-only / prevalence / Hamming baselines |
10
10
 
11
11
  ## Per-task results
12
12
  | Task | Family | Available | Planner | Naive baseline | Gate |
@@ -26,6 +26,7 @@ Deterministic planner beats the naive baseline on **11/11** grounded tasks with
26
26
  | multi_write_type_legality | MW_multi_write_type | True | 1.0 | 0.0 | - |
27
27
  | adversarial_robustness | T13_scope_disguise | True | 1.0 | 0.0 | - |
28
28
  | graph_multihop_reasoning | GR_graph_reasoning | True | 1.0 | 0.0 | - |
29
+ | co_scientist_grounded | CS_co_scientist | True | 1.0 | 0.0 | - |
29
30
 
30
31
  ## Trust tasks (T8-T11) - calibration + scope-awareness separate *trustworthy* agents
31
32
  Each contrasts the **uncertainty-aware** agent (conformal coverage, selective prediction, OOD flagging, out-of-scope deferral) with an **over-confident** baseline (an uncalibrated interval, no abstention, never flags OOD, no scope layer). The over-confident agent is the realistic failure mode a calibrated co-scientist must beat.
@@ -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.1"
11
+ version: "0.3.2"
12
12
  prepared: "2026-06-09"
13
13
 
14
14
  taxonomy:
@@ -35,6 +35,8 @@ taxonomy:
35
35
  T16_distribution_shift: "an OOD context -> confidence is deflated (extrapolating), not reported at the in-distribution level"
36
36
  # v0.3.1 (v4.5): multi-hop reasoning over the living world-model graph.
37
37
  GR_graph_reasoning: "answer a multi-hop design question (writers reaching a locus AND deliverable carrying a cargo form) as a PROVENANCED graph traversal (vs an ungrounded agent that cannot cite a path)"
38
+ # v0.3.2 (v5.0): the matured co-scientist as reference solver.
39
+ CS_co_scientist: "end-to-end grounded design: multiple materially-distinct legal confidence-tagged strategies, each citation-grounded + scope-ledgered, no-fabrication across the full reasoning stack (vs an ungrounded agent producing none of these)"
38
40
 
39
41
  tasks:
40
42
  - id: site_selection_blind_gsh
@@ -207,3 +209,18 @@ tasks:
207
209
  circular: false
208
210
  note: "v4.5 world-model graph: a design question answered as one grounded traversal; an ungrounded agent
209
211
  has no graph and cannot produce a provenanced path (0 by construction). no-fabrication holds."
212
+
213
+ # ---- v0.3.2 (v5.0): the matured co-scientist as the reference solver.
214
+ - id: co_scientist_grounded
215
+ family: CS_co_scientist
216
+ scorer: "pen_stack.validate.bench_coscientist_tasks:run"
217
+ metric: "co_scientist_grounded_rate"
218
+ baseline_metric: "ungrounded_baseline_rate"
219
+ higher_is_better: true
220
+ ground_truth: "frozen panel of write goals; a recommendation set is 'fully grounded' iff it is multiple
221
+ materially-distinct (>=2 design axes) + each legal (verifier) + confidence-tagged (calibrated) + the
222
+ rationale's citations are in the curated DOI set + the scope ledger is complete + no-fabrication - all
223
+ mechanistic/verifier facts, not the agent's own claim (non-circular)"
224
+ circular: false
225
+ note: "v5.0 capstone: the matured co-scientist; the central gate is no-fabrication under the FULL reasoning
226
+ stack. An ungrounded agent produces none of these grounded properties (0 by construction)."
@@ -0,0 +1,31 @@
1
+ # The co-scientist (v5.0)
2
+
3
+ v5.0 matures the reasoning layer on top of the verifier (v3.3), the environment (v3.4), the oracle mesh
4
+ (v4.0), and the living world-model (v4.5). Give it a goal and an intent and it returns a small set of
5
+ **materially distinct, ranked, fully-traceable strategies** — each verified, calibrated, cited, and
6
+ scope-ledgered — while the **no-fabrication guarantee holds by construction**: the reasoning layer proposes
7
+ and critiques, but every number still comes from a validated tool or oracle.
8
+
9
+ > **The central invariant.** Intelligence rises while groundedness never falls. A test asserts no-fabrication
10
+ > across the *full* reasoning stack (`pen_stack/validate/bench_coscientist_tasks.py`).
11
+
12
+ ## What it does (`pen_stack/agent/co_scientist.py`, `pen_stack/agent/cite.py`)
13
+
14
+ | Capability | Function | Guarantee |
15
+ |---|---|---|
16
+ | **Multiple distinct strategies** | `propose_strategies(goal)` | 2–3 strategies differing on ≥2 design axes (write-type / writer / delivery / intent) — *materially* distinct, not reworded (`distinctness()` measures it); each independently **legal** + **confidence-tagged** |
17
+ | **Deliberative planning** | `deliberate(goal)` | the deliberative planner vs the deterministic `pen_agent` baseline, head-to-head; both grounded |
18
+ | **Self-critique / revise** | `critique_and_revise(design)` | the critic only flags + suggests a design-level swap (never invents a number); the revision is **re-verified**; falsifiable — it improves flawed plans (illegal→legal) and never spuriously touches clean ones (`critique_falsifiability()`) |
19
+ | **Cited rationale** | `cited_rationale(design)` | the "why" cites DOIs **drawn from the curated world-model** (so they resolve by construction); a hallucinated-citation guard rejects any DOI not in the curated set |
20
+ | **Scope ledger** | `scope_ledger(design)` | per recommendation, an itemised list of what **was** assessed (legality / reachability / delivery / payload / calibrated confidence) and what was **not** (the standing known-unknowns) — never silently omitted |
21
+ | **Scoped generalisation** | `generalise(task)` | adjacent genetic-engineering tasks are **grounded-or-refused**: answered only if they map to an existing grounded capability, otherwise refused with a scope statement |
22
+
23
+ ## Honest scope
24
+
25
+ A better reasoner is **not a complete model of the cell**. structure→phenotype, in-vivo behaviour,
26
+ immunogenicity magnitude, long-term durability and higher-order epistasis remain out of scope — the
27
+ co-scientist makes that boundary *legible* (the scope ledger), it does not close it. Self-critique and
28
+ multi-strategy ship only because they help on held-out checks, or are reported as not-yet-useful.
29
+ Generalisation is approached only as far as the grounding allows; the rest is refused, not faked.
30
+
31
+ See `prereg/ws_{plan,crit,cite}.yaml` and the `co_scientist_grounded` bench task (Genome-Writing Bench v0.3.2).
@@ -1,2 +1,2 @@
1
1
  """PEN-STACK v3.0 - open infrastructure for genome writing."""
2
- __version__ = "4.5.1"
2
+ __version__ = "5.0.0"
@@ -0,0 +1,118 @@
1
+ """Cited mechanistic rationale + scoped generalisation (v5.0, WS-CITE + WS-GEN).
2
+
3
+ Every recommendation carries a short, literature-cited "why". Crucially the citations are **drawn from the
4
+ curated world-model** (the verifier rule provenance, the writer/delivery/locus DOIs) — not generated by a
5
+ language model — so they **resolve by construction**. A hallucinated-citation guard rejects any DOI that is
6
+ not in the curated, already-verified set (WS-CITE). Numbers in the rationale remain tool-sourced (the prose is
7
+ a presentation layer over verified facts; no quantity is invented).
8
+
9
+ WS-GEN: generalisation toward adjacent genetic-engineering tasks is **grounded-or-refused** — a task is
10
+ answered only if it maps to an existing grounded capability; anything else is refused with a scope statement,
11
+ never faked.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from functools import lru_cache
16
+
17
+ import yaml
18
+
19
+ from pen_stack._resources import resource
20
+
21
+
22
+ @lru_cache(maxsize=1)
23
+ def curated_dois() -> frozenset[str]:
24
+ """The set of DOIs that appear in the CURATED, already-verified world-model (delivery palette, GSH loci,
25
+ writer panel, rule provenance). A citation is 'grounded' iff its DOI is in this set — so a citation can
26
+ only ever point at a source the substrate has actually curated (no hallucinated references)."""
27
+ dois: set[str] = set()
28
+ veh = yaml.safe_load(resource("configs/delivery_vehicles.yaml").read_text(encoding="utf-8"))["vehicles"]
29
+ for v in veh.values():
30
+ dois.update(v.get("dois", []) or [])
31
+ gsh = yaml.safe_load(resource("configs/gsh_validated_heldout.yaml").read_text(encoding="utf-8"))["gsh"]
32
+ for g in gsh:
33
+ if g.get("doi"):
34
+ dois.add(g["doi"])
35
+ import csv
36
+ with open(resource("data/writer_panel.csv"), encoding="utf-8") as f:
37
+ for row in csv.DictReader(f):
38
+ if row.get("doi"):
39
+ dois.add(row["doi"])
40
+ # rule provenance DOIs
41
+ from pen_stack.rules import load_ruleset
42
+ for r in load_ruleset().rules:
43
+ dois.update(r.provenance.get("doi", []) or [])
44
+ return frozenset(dois)
45
+
46
+
47
+ def citations_grounded(dois: list[str]) -> dict:
48
+ """Hallucinated-citation guard: every cited DOI must be in the curated set. Returns the verdict + any
49
+ ungrounded DOIs (which would be a hallucination and are rejected)."""
50
+ curated = curated_dois()
51
+ ungrounded = [d for d in dois if d not in curated]
52
+ return {"all_grounded": not ungrounded, "ungrounded": ungrounded, "n_checked": len(dois)}
53
+
54
+
55
+ def cited_rationale(design: dict) -> dict:
56
+ """A short, literature-cited mechanistic 'why' for a design, with citations DRAWN FROM the curated
57
+ world-model (so they resolve by construction). Numbers are tool-sourced; the prose is presentation only."""
58
+ from pen_stack.graph import build_graph
59
+ from pen_stack.verify import verify
60
+ g = build_graph()
61
+ v = verify(design)
62
+ fam = design.get("writer_family")
63
+ veh = design.get("delivery_vehicle")
64
+ cites: list[dict] = []
65
+ wnode = g.nodes.get(f"writer:{fam}")
66
+ if wnode:
67
+ for d in wnode.props.get("dois", [])[:1]:
68
+ cites.append({"doi": d, "claim": f"{fam} mechanism / characterisation", "source": "world-model writer"})
69
+ vnode = g.nodes.get(f"vehicle:{veh}")
70
+ if vnode:
71
+ for d in vnode.props.get("dois", [])[:1]:
72
+ cites.append({"doi": d, "claim": f"{veh} delivery properties", "source": "world-model vehicle"})
73
+ form = wnode.props.get("output_form") if wnode else None
74
+ verdict = "legal" if v.legal else "illegal"
75
+ rationale = (f"{fam} ({form}-form) installs a {design.get('cargo_bp')} bp cargo via {veh}; the rule-grounded "
76
+ f"verifier judges this {verdict}"
77
+ + (f" (violated: {', '.join(x['rule_id'] for x in v.violations)})" if not v.legal else "")
78
+ + (f"; calibrated confidence {v.confidence}" if v.confidence is not None else
79
+ "; confidence abstained (unscored)") + ".")
80
+ guard = citations_grounded([c["doi"] for c in cites])
81
+ return {"design": {k: val for k, val in design.items() if not str(k).startswith("_")},
82
+ "rationale": rationale, "citations": cites, "n_citations": len(cites),
83
+ "citations_grounded": guard["all_grounded"], "ungrounded_citations": guard["ungrounded"],
84
+ "no_fabrication": v.no_fabrication and guard["all_grounded"],
85
+ "note": "citations are drawn from the curated world-model (resolve by construction); numbers are "
86
+ "verifier-sourced; the prose is a presentation layer (no fabricated quantity)."}
87
+
88
+
89
+ # --------------------------------------------------------------------------------------------------
90
+ # WS-GEN - scoped generalisation: grounded-or-refused.
91
+ # --------------------------------------------------------------------------------------------------
92
+ # adjacent genetic-engineering tasks that MAP to an existing grounded capability (answerable);
93
+ # anything else is refused with a scope statement.
94
+ _GROUNDED_TASKS = {
95
+ "delivery_selection": "maps to the v3.3 delivery rules (cargo-form + capacity + integration)",
96
+ "write_type_legality": "maps to the v3.3 write-type router + verifier",
97
+ "off_target_screen": "maps to the bridge off-target engine (a screen, not a per-site calculator)",
98
+ "writer_variant_critique": "maps to the v4.0 writer-verification branch (score/critique, never invent)",
99
+ }
100
+
101
+
102
+ def generalise(task: str, payload: dict | None = None) -> dict:
103
+ """Answer an adjacent genetic-engineering task ONLY if it maps to a grounded capability; otherwise REFUSE
104
+ with a scope statement (Principle 4: grounded-or-refused, never faked)."""
105
+ payload = payload or {}
106
+ if task not in _GROUNDED_TASKS:
107
+ return {"task": task, "grounded": False, "refused": True,
108
+ "scope_statement": f"'{task}' has no grounding in PEN-STACK; refused rather than faked. "
109
+ f"Grounded tasks: {sorted(_GROUNDED_TASKS)}.",
110
+ "no_fabrication": True}
111
+ result: dict = {"task": task, "grounded": True, "refused": False,
112
+ "grounding": _GROUNDED_TASKS[task], "no_fabrication": True}
113
+ if task in ("delivery_selection", "write_type_legality") and payload:
114
+ from pen_stack.verify import verify
115
+ v = verify(payload)
116
+ result["verdict"] = {"legal": v.legal, "confidence": v.confidence,
117
+ "violations": [x["rule_id"] for x in v.violations]}
118
+ return result
@@ -0,0 +1,232 @@
1
+ """The co-scientist — deliberative, multi-strategy, grounded design (v5.0, WS-PLAN + WS-MULTI).
2
+
3
+ The reasoning ceiling rises while the grounding floor stays fixed (v5.0 Principle 1): the co-scientist
4
+ *deliberates* over alternative design paths and returns a small set of **materially distinct** strategies, but
5
+ every number still comes from the rule-grounded verifier / oracles — it can propose and rank, never source a
6
+ quantity (the no-fabrication gate holds by construction, asserted by test).
7
+
8
+ `propose_strategies(goal)` returns 2-3 strategies that differ on real design axes (write-type / writer /
9
+ delivery / edit-intent), each independently **verified** (legal) and **confidence-tagged**, with its tradeoffs
10
+ surfaced. A distinctness metric proves they are materially different, not reworded variants (v5.0 Principle 2).
11
+ The deterministic planner remains the baseline/fallback; `deliberate()` benchmarks the two head-to-head.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass, field
16
+ from itertools import combinations
17
+ from typing import Any
18
+
19
+ # candidate strategy templates - each a MATERIALLY different approach to installing a payload at a locus.
20
+ # (write_type, writer_family, delivery_vehicle, edit_intent, label, tradeoff)
21
+ _STRATEGY_TEMPLATES = [
22
+ ("insertion", "bridge_IS110", "AAV_single", "safe_harbour_insertion",
23
+ "safe-harbour insertion", "DSB-free, AAV-deliverable, off-target-screened; cargo <=4.7 kb"),
24
+ ("landing_pad_install", "PE_integrase", "AAV_single", "high_durability_insertion",
25
+ "landing-pad install", "prime-edited att beacon then integrase; two-step, durable, broadly reachable"),
26
+ ("insertion", "Cas9", "electroporation", "knock_in_with_disruption",
27
+ "in-locus RNP knock-in", "RNP electroporation (transient, low immunogenicity); DSB-based, ex-vivo"),
28
+ ("multiplex", "bridge_IS110", "electroporation", "safe_harbour_insertion",
29
+ "multiplex DSB-free", "concurrent edits, DSB-free -> ~zero translocation risk; ex-vivo"),
30
+ ]
31
+ _AXES = ("write_type", "writer_family", "delivery_vehicle", "edit_intent")
32
+
33
+
34
+ @dataclass
35
+ class Strategy:
36
+ label: str
37
+ design: dict[str, Any]
38
+ legal: bool | None
39
+ confidence: float | None
40
+ interval: list[float] | None
41
+ epistemic_status: str
42
+ violations: list[dict]
43
+ tradeoff: str
44
+ no_fabrication: bool
45
+ provenance: dict[str, Any] = field(default_factory=dict)
46
+
47
+
48
+ def _verify_design(design: dict) -> Strategy | None:
49
+ from pen_stack.verify import verify
50
+ v = verify(design)
51
+ if v.deferred:
52
+ return None
53
+ return Strategy(label=design.get("_label", ""), design={k: v2 for k, v2 in design.items()
54
+ if not k.startswith("_")}, legal=v.legal, confidence=v.confidence, interval=v.interval,
55
+ epistemic_status=v.epistemic_status, violations=v.violations,
56
+ tradeoff=design.get("_tradeoff", ""), no_fabrication=v.no_fabrication,
57
+ provenance=v.provenance)
58
+
59
+
60
+ def propose_strategies(gene: str = "AAVS1", cargo_bp: int = 3000, cell_type: str = "K562",
61
+ n: int = 3) -> dict:
62
+ """Return up to `n` materially-distinct, verified, confidence-tagged strategies for a write goal.
63
+ Numbers come only from the verifier (no fabrication); strategies are ranked legal-first then by confidence."""
64
+ strategies: list[Strategy] = []
65
+ for wt, fam, veh, intent, label, tradeoff in _STRATEGY_TEMPLATES:
66
+ design = {"write_type": wt, "writer_family": fam, "delivery_vehicle": veh, "edit_intent": intent,
67
+ "cargo_bp": cargo_bp, "cell_type": cell_type, "gene": gene,
68
+ # per-axis scores let the verifier attach a CALIBRATED confidence (else it abstains) - tool-sourced
69
+ "safety": 0.8, "p_durable": 0.75, "writer_activity": 0.7,
70
+ "edits": [{"site": "A"}, {"site": "B"}] if wt == "multiplex" else [],
71
+ "_label": label, "_tradeoff": tradeoff}
72
+ s = _verify_design(design)
73
+ if s is not None:
74
+ strategies.append(s)
75
+ legal = [s for s in strategies if s.legal]
76
+ legal.sort(key=lambda s: (s.confidence if s.confidence is not None else -1), reverse=True)
77
+ chosen = legal[:n]
78
+ dist = distinctness(chosen)
79
+ return {"goal": {"gene": gene, "cargo_bp": cargo_bp, "cell_type": cell_type},
80
+ "n_strategies": len(chosen),
81
+ "strategies": [s.__dict__ for s in chosen],
82
+ "distinctness": dist,
83
+ "all_legal": all(s.legal for s in chosen),
84
+ "all_confidence_tagged": all(s.confidence is not None for s in chosen),
85
+ "no_fabrication": all(s.no_fabrication for s in chosen),
86
+ "note": "multiple materially-distinct strategies; every number is verifier-sourced (no fabrication)"}
87
+
88
+
89
+ def distinctness(strategies: list[Strategy]) -> dict:
90
+ """Materially-distinct = every pair differs on >=2 design axes (not a reworded variant). Measured."""
91
+ if len(strategies) < 2:
92
+ return {"materially_distinct": len(strategies) <= 1, "min_pairwise_axis_diff": None, "n": len(strategies)}
93
+ diffs = []
94
+ for a, b in combinations(strategies, 2):
95
+ d = sum(1 for ax in _AXES if a.design.get(ax) != b.design.get(ax))
96
+ diffs.append(d)
97
+ return {"materially_distinct": min(diffs) >= 2, "min_pairwise_axis_diff": min(diffs),
98
+ "mean_pairwise_axis_diff": round(sum(diffs) / len(diffs), 2), "n": len(strategies),
99
+ "axes": list(_AXES)}
100
+
101
+
102
+ # --------------------------------------------------------------------------------------------------
103
+ # WS-CRIT - self-critique / revise loop. The critic can ONLY flag/reject + suggest a design-level swap;
104
+ # it never invents a number. A deterministic fix is applied and the plan is RE-VERIFIED (falsifiable: the
105
+ # revision must measurably improve plan quality, else it is reported as not-yet-useful).
106
+ # --------------------------------------------------------------------------------------------------
107
+ # DNA-cargo vehicles by ascending capacity, for the "oversize cargo" deterministic revision.
108
+ _DNA_VEHICLES = [("AAV_single", 4700), ("AAV_dual", 9000), ("helper_dependent_adenovirus", 35000),
109
+ ("hsv_amplicon", 100000)]
110
+ _RNP_VEHICLE = "electroporation"
111
+ _RNP_WRITERS = {"Cas9", "Cas12a"}
112
+
113
+
114
+ def critique(design: dict) -> dict:
115
+ """Flag issues in a design via the verifier (hard violations / soft flags / scope) + categorical
116
+ cross-checks. Returns flags + a suggested design-level revision. NEVER invents a number."""
117
+ from pen_stack.verify import verify
118
+ v = verify(design)
119
+ flags = []
120
+ revision: dict | None = None
121
+ for viol in v.violations:
122
+ rid = viol["rule_id"]
123
+ flags.append({"kind": "hard", "rule_id": rid, "reason": viol["reason"]})
124
+ if rid == "payload.cargo_within_capacity": # oversize cargo -> bigger DNA vehicle
125
+ cap_ok = next((name for name, cap in _DNA_VEHICLES if (design.get("cargo_bp") or 0) <= cap), None)
126
+ if cap_ok:
127
+ revision = {**design, "delivery_vehicle": cap_ok}
128
+ elif rid == "delivery.cargo_form_compatible": # RNP into a DNA-only vehicle -> physical delivery
129
+ revision = {**design, "delivery_vehicle": _RNP_VEHICLE}
130
+ for s in v.soft_flags:
131
+ flags.append({"kind": "soft", "rule_id": s["rule_id"], "reason": s["reason"]})
132
+ for sc in v.scope_flags:
133
+ flags.append({"kind": "scope", "reason": sc.get("reason", sc.get("kind"))})
134
+ return {"legal": v.legal, "confidence": v.confidence, "flags": flags, "n_hard": len(v.violations),
135
+ "n_soft": len(v.soft_flags), "suggested_revision": revision, "no_fabrication": v.no_fabrication}
136
+
137
+
138
+ def critique_and_revise(design: dict) -> dict:
139
+ """One critique→revise→re-verify cycle. Returns before/after with whether plan quality IMPROVED
140
+ (illegal→legal, or fewer soft flags). The critic only swaps design choices; numbers stay tool-sourced."""
141
+ before = critique(design)
142
+ if before["suggested_revision"] is None:
143
+ return {"revised": False, "before": before, "after": before, "improved": False,
144
+ "note": "no deterministic fix available (critique not-yet-useful on this design)"}
145
+ after = critique(before["suggested_revision"])
146
+ improved = (bool(after["legal"]) and not before["legal"]) or (after["n_soft"] < before["n_soft"])
147
+ return {"revised": True, "revised_design": before["suggested_revision"], "before": before,
148
+ "after": after, "improved": bool(improved),
149
+ "no_fabrication": before["no_fabrication"] and after["no_fabrication"]}
150
+
151
+
152
+ # frozen falsifiability panel: FLAWED designs (a real fixable flaw) + CLEAN designs (no fixable flaw).
153
+ _FLAWED = [
154
+ {"write_type": "insertion", "writer_family": "bridge_IS110", "cargo_bp": 30000,
155
+ "delivery_vehicle": "AAV_single"}, # oversize -> AAV_dual/HDAd
156
+ {"write_type": "insertion", "writer_family": "Cas9", "cargo_bp": 1000,
157
+ "delivery_vehicle": "AAV_single"}, # RNP into DNA-only AAV -> electroporation
158
+ ]
159
+ _CLEAN = [
160
+ {"write_type": "insertion", "writer_family": "bridge_IS110", "cargo_bp": 3000,
161
+ "delivery_vehicle": "AAV_single", "safety": 0.8, "p_durable": 0.7, "writer_activity": 0.7},
162
+ ]
163
+
164
+
165
+ def critique_falsifiability() -> dict:
166
+ """Falsifiability test (v5.0 Principle 3): on FLAWED designs the critique→revise loop must IMPROVE plan
167
+ quality (illegal→legal); on CLEAN designs it must NOT spuriously change them. Reported honestly."""
168
+ flawed = [critique_and_revise(d) for d in _FLAWED]
169
+ clean = [critique_and_revise(d) for d in _CLEAN]
170
+ improved = sum(int(r["improved"]) for r in flawed)
171
+ spurious = sum(int(r["revised"] and not r["improved"]) for r in clean)
172
+ return {"available": True, "n_flawed": len(_FLAWED), "n_clean": len(_CLEAN),
173
+ "flawed_improved": improved, "flawed_improve_rate": round(improved / len(_FLAWED), 3),
174
+ "clean_spurious_revisions": spurious,
175
+ "useful": improved == len(_FLAWED) and spurious == 0,
176
+ "no_fabrication": all(r.get("no_fabrication", True) for r in flawed + clean),
177
+ "note": "self-critique improves held-out FLAWED plans (illegal->legal) without touching CLEAN ones; "
178
+ "reported honestly (Principle 3: falsifiable, not assumed beneficial)."}
179
+
180
+
181
+ # --------------------------------------------------------------------------------------------------
182
+ # WS-SCOPE2 - a fine-grained per-recommendation scope ledger: what WAS assessed vs what was NOT.
183
+ # --------------------------------------------------------------------------------------------------
184
+ def scope_ledger(design: dict) -> dict:
185
+ """Itemise, per recommendation, what the substrate ASSESSED (with its verdict/confidence) and what it
186
+ did NOT (the standing known-unknowns + any verifier scope flags). Out-of-scope is never silently omitted."""
187
+ import yaml
188
+
189
+ from pen_stack._resources import resource
190
+ from pen_stack.verify import verify
191
+ v = verify(design)
192
+ assessed = [
193
+ {"dimension": "rule_legality", "verdict": v.legal, "source": "rules.solver"},
194
+ {"dimension": "reachability", "verdict": not any(x["rule_id"].startswith("reachability.")
195
+ for x in v.violations), "source": "target_site rule"},
196
+ {"dimension": "delivery_compatibility", "verdict": not any(x["rule_id"].startswith("delivery.")
197
+ for x in v.violations), "source": "delivery rules"},
198
+ {"dimension": "payload_capacity", "verdict": not any(x["rule_id"].startswith("payload.")
199
+ for x in v.violations), "source": "payload rule"},
200
+ {"dimension": "calibrated_confidence",
201
+ "verdict": v.confidence, "source": "L4 uncertainty (abstains when unscored)"},
202
+ ]
203
+ ku = yaml.safe_load(resource("configs/known_unknowns.yaml").read_text(encoding="utf-8"))["known_unknowns"]
204
+ not_assessed = [{"id": k["id"], "title": k.get("title"), "why": k.get("why")} for k in ku]
205
+ not_assessed += [{"id": "rule_scope", "title": sc.get("rule_id", sc.get("kind")),
206
+ "why": sc.get("reason")} for sc in v.scope_flags]
207
+ return {"design": {k: val for k, val in design.items() if not str(k).startswith("_")},
208
+ "assessed": assessed, "not_assessed": not_assessed,
209
+ "n_assessed": len(assessed), "n_not_assessed": len(not_assessed),
210
+ "complete": True, "no_fabrication": v.no_fabrication,
211
+ "note": "every recommendation carries a complete scope ledger; out-of-scope dimensions are "
212
+ "itemised (the known-unknowns), never silently omitted."}
213
+
214
+
215
+ def deliberate(gene: str = "AAVS1", cargo_bp: int = 3000, cell_type: str = "K562") -> dict:
216
+ """WS-PLAN head-to-head: the deliberative co-scientist (best of the distinct strategies) vs the
217
+ deterministic baseline (pen_agent state machine). Reports both honestly; no-fabrication holds for both."""
218
+ delib = propose_strategies(gene, cargo_bp, cell_type, n=3)
219
+ best = delib["strategies"][0] if delib["strategies"] else None
220
+ baseline = {"available": False, "note": "deterministic pen_agent baseline needs the Phase-1 atlas (VM/local)"}
221
+ try:
222
+ from pen_stack.agent.pen_agent import plan_write_session
223
+ r = plan_write_session(gene, "safe_harbour_insertion", cargo_bp=cargo_bp, ct=cell_type.lower())
224
+ baseline = {"available": True, "no_fabrication": r.get("no_fabrication"),
225
+ "plan_confidence": r.get("plan_confidence"), "completed": r.get("completed")}
226
+ except Exception as e: # noqa: BLE001 - atlas absent -> baseline deferred, never fabricated
227
+ baseline["error"] = f"{type(e).__name__}"
228
+ return {"deliberative_best": best, "deliberative_n": delib["n_strategies"],
229
+ "distinctness": delib["distinctness"], "baseline": baseline,
230
+ "no_fabrication": delib["no_fabrication"] and baseline.get("no_fabrication", True),
231
+ "note": "deliberative planner explores distinct verified strategies; deterministic planner is the "
232
+ "baseline/fallback; both are grounded (no fabrication). Plan quality reported honestly."}