agentevolve-optimizer 0.5.0__py3-none-any.whl

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 (414) hide show
  1. agent_evolve/__init__.py +722 -0
  2. agent_evolve/agentic.py +2800 -0
  3. agent_evolve/api.py +767 -0
  4. agent_evolve/application/__init__.py +1580 -0
  5. agent_evolve/application/action_allocation.py +744 -0
  6. agent_evolve/application/action_allocation_frame.py +347 -0
  7. agent_evolve/application/action_allocation_frame_commit.py +185 -0
  8. agent_evolve/application/action_allocation_frame_commit_v3.py +184 -0
  9. agent_evolve/application/action_allocation_frame_v3.py +338 -0
  10. agent_evolve/application/action_archive_value.py +497 -0
  11. agent_evolve/application/action_evidence_consistency.py +455 -0
  12. agent_evolve/application/action_forecast_partitioning.py +1471 -0
  13. agent_evolve/application/action_metric_projection.py +211 -0
  14. agent_evolve/application/action_role_value.py +680 -0
  15. agent_evolve/application/action_score_authorities.py +363 -0
  16. agent_evolve/application/action_structural_signature.py +116 -0
  17. agent_evolve/application/action_target_realization.py +402 -0
  18. agent_evolve/application/agentic_evolution.py +7734 -0
  19. agent_evolve/application/agentic_portfolio_residual_expert.py +835 -0
  20. agent_evolve/application/anchor_residual_identification.py +463 -0
  21. agent_evolve/application/archive_conditioned_action_target.py +208 -0
  22. agent_evolve/application/artifact_journal.py +246 -0
  23. agent_evolve/application/artifact_replay.py +347 -0
  24. agent_evolve/application/budgeted_optimizer.py +1828 -0
  25. agent_evolve/application/calibrated_campaign.py +485 -0
  26. agent_evolve/application/calibrated_current_prefix_forecast_opportunity.py +322 -0
  27. agent_evolve/application/calibrated_positive_gain_opportunity.py +1581 -0
  28. agent_evolve/application/campaign_capacity_recourse.py +254 -0
  29. agent_evolve/application/campaign_contextual_outcomes.py +119 -0
  30. agent_evolve/application/campaign_diagnostic_blocks.py +930 -0
  31. agent_evolve/application/campaign_evidence_registry.py +262 -0
  32. agent_evolve/application/campaign_execution.py +2537 -0
  33. agent_evolve/application/campaign_generation_audit.py +942 -0
  34. agent_evolve/application/campaign_learning.py +1812 -0
  35. agent_evolve/application/campaign_learning_runtime.py +1977 -0
  36. agent_evolve/application/campaign_search_phase.py +227 -0
  37. agent_evolve/application/campaign_selector_context_extension.py +220 -0
  38. agent_evolve/application/campaign_variation_envelope.py +649 -0
  39. agent_evolve/application/campaign_variation_trace.py +451 -0
  40. agent_evolve/application/candidate_archive_consequence.py +128 -0
  41. agent_evolve/application/causal_opportunity_portfolio_gate.py +385 -0
  42. agent_evolve/application/composite_outcome_updater.py +145 -0
  43. agent_evolve/application/composition_portfolio_selection.py +363 -0
  44. agent_evolve/application/concurrent_stage.py +144 -0
  45. agent_evolve/application/contextual_action_allocation.py +181 -0
  46. agent_evolve/application/contextual_campaign_outcomes.py +267 -0
  47. agent_evolve/application/contextual_campaign_planning.py +1366 -0
  48. agent_evolve/application/contextual_delayed_credit.py +651 -0
  49. agent_evolve/application/contextual_search_controller.py +2374 -0
  50. agent_evolve/application/current_prefix_forecast_opportunity.py +714 -0
  51. agent_evolve/application/decision_metric_projection.py +112 -0
  52. agent_evolve/application/derived_action_semantics.py +129 -0
  53. agent_evolve/application/detailed_evaluation.py +449 -0
  54. agent_evolve/application/earned_lineage.py +1011 -0
  55. agent_evolve/application/effective_choice_audit.py +484 -0
  56. agent_evolve/application/empirical_consequence_calibration.py +908 -0
  57. agent_evolve/application/evaluation_accounting.py +325 -0
  58. agent_evolve/application/evaluation_cache.py +199 -0
  59. agent_evolve/application/evaluation_escrow.py +547 -0
  60. agent_evolve/application/evaluation_recourse.py +253 -0
  61. agent_evolve/application/event_recorder.py +151 -0
  62. agent_evolve/application/evolution_campaign.py +1840 -0
  63. agent_evolve/application/executable_hypothesis.py +323 -0
  64. agent_evolve/application/factorial_branch_pilot.py +772 -0
  65. agent_evolve/application/finite_acquisition_capacity_recourse.py +672 -0
  66. agent_evolve/application/finite_acquisition_residual_expert.py +373 -0
  67. agent_evolve/application/finite_acquisition_variation_envelope.py +802 -0
  68. agent_evolve/application/finite_action_hypothesis_semantics.py +446 -0
  69. agent_evolve/application/finite_action_selection.py +188 -0
  70. agent_evolve/application/finite_action_set.py +306 -0
  71. agent_evolve/application/finite_action_transition.py +537 -0
  72. agent_evolve/application/finite_variation_eligibility.py +296 -0
  73. agent_evolve/application/forecast_geometry_portfolio.py +799 -0
  74. agent_evolve/application/forecast_opportunity_shadow_calibration.py +316 -0
  75. agent_evolve/application/front_proximity_admission.py +311 -0
  76. agent_evolve/application/front_proximity_parent_basis.py +458 -0
  77. agent_evolve/application/frozen_hurdle_score.py +659 -0
  78. agent_evolve/application/g3_causal_screen.py +2257 -0
  79. agent_evolve/application/g3_causal_validation.py +1046 -0
  80. agent_evolve/application/g3_postseal_curation.py +818 -0
  81. agent_evolve/application/gated_agentic_generator.py +205 -0
  82. agent_evolve/application/generation_feedback.py +293 -0
  83. agent_evolve/application/generative_proposal_journal.py +185 -0
  84. agent_evolve/application/geometry_conditional_elasticity.py +453 -0
  85. agent_evolve/application/global_wave_action_allocation.py +1151 -0
  86. agent_evolve/application/head_mass_conditional_seat.py +268 -0
  87. agent_evolve/application/identifiable_reflection_evidence.py +1147 -0
  88. agent_evolve/application/identifiable_reflection_learning.py +395 -0
  89. agent_evolve/application/identifiable_reflection_request.py +364 -0
  90. agent_evolve/application/in_memory_residual_archive.py +341 -0
  91. agent_evolve/application/insight_memory.py +1804 -0
  92. agent_evolve/application/live_runtime_manifest.py +758 -0
  93. agent_evolve/application/llm_task_queue.py +769 -0
  94. agent_evolve/application/matched_finite_action_block.py +409 -0
  95. agent_evolve/application/materialized_action_broker.py +2328 -0
  96. agent_evolve/application/materialized_action_constraints.py +83 -0
  97. agent_evolve/application/materialized_variation.py +211 -0
  98. agent_evolve/application/multi_option_evolution.py +1536 -0
  99. agent_evolve/application/outcome_adaptive_action_racing.py +2827 -0
  100. agent_evolve/application/outcome_adaptive_residual_campaign_runtime.py +580 -0
  101. agent_evolve/application/outcome_adaptive_residual_portfolio_evolution.py +3671 -0
  102. agent_evolve/application/outcome_conditioned_portfolio_selection.py +1374 -0
  103. agent_evolve/application/outcome_relation.py +193 -0
  104. agent_evolve/application/paired_allocation_comparison.py +241 -0
  105. agent_evolve/application/paired_block_schedule.py +127 -0
  106. agent_evolve/application/parent_measurement.py +226 -0
  107. agent_evolve/application/pareto_archive.py +811 -0
  108. agent_evolve/application/portfolio_campaign_runtime.py +4739 -0
  109. agent_evolve/application/portfolio_evolution.py +2950 -0
  110. agent_evolve/application/portfolio_hypothesis_observations.py +814 -0
  111. agent_evolve/application/portfolio_memory_attribution.py +581 -0
  112. agent_evolve/application/portfolio_memory_dose.py +788 -0
  113. agent_evolve/application/portfolio_memory_matched_control.py +938 -0
  114. agent_evolve/application/portfolio_memory_transfer.py +297 -0
  115. agent_evolve/application/portfolio_optimization_memory.py +363 -0
  116. agent_evolve/application/portfolio_outcome_feedback.py +1613 -0
  117. agent_evolve/application/portfolio_projection.py +335 -0
  118. agent_evolve/application/portfolio_recombination.py +2032 -0
  119. agent_evolve/application/post_evolution_reflection.py +834 -0
  120. agent_evolve/application/postcommit_rank_authority.py +245 -0
  121. agent_evolve/application/precommitted_portfolio_racing.py +2762 -0
  122. agent_evolve/application/prequential_archive_opportunity_calibration.py +1154 -0
  123. agent_evolve/application/prequential_residual_exploration.py +343 -0
  124. agent_evolve/application/prequential_score_portfolio.py +954 -0
  125. agent_evolve/application/projections.py +292 -0
  126. agent_evolve/application/protected_action_committee.py +1027 -0
  127. agent_evolve/application/protected_branch_pilot.py +376 -0
  128. agent_evolve/application/protected_current_prefix_forecast_opportunity.py +552 -0
  129. agent_evolve/application/provider_replay.py +910 -0
  130. agent_evolve/application/rank_balanced_causal_pilot.py +1372 -0
  131. agent_evolve/application/recombination_residual_expert.py +403 -0
  132. agent_evolve/application/reflection_workflow.py +571 -0
  133. agent_evolve/application/region_conditional_credit.py +911 -0
  134. agent_evolve/application/residual_campaign_runtime.py +531 -0
  135. agent_evolve/application/residual_headroom_campaign_runtime.py +459 -0
  136. agent_evolve/application/residual_headroom_ledger.py +1544 -0
  137. agent_evolve/application/residual_learning_transaction.py +396 -0
  138. agent_evolve/application/residual_portfolio_evolution.py +1228 -0
  139. agent_evolve/application/residual_reachability.py +749 -0
  140. agent_evolve/application/residual_stage_credit.py +499 -0
  141. agent_evolve/application/same_prefix_paired_audit.py +1580 -0
  142. agent_evolve/application/semantic_coverage_score_portfolio.py +838 -0
  143. agent_evolve/application/sequential_lineage_allocation.py +1017 -0
  144. agent_evolve/application/sequential_market_replay.py +1395 -0
  145. agent_evolve/application/sequential_residual_campaign_runtime.py +305 -0
  146. agent_evolve/application/sequential_residual_portfolio_evolution.py +940 -0
  147. agent_evolve/application/single_score_action_allocation.py +299 -0
  148. agent_evolve/application/source_exposure_allocation.py +906 -0
  149. agent_evolve/application/staged_memory.py +210 -0
  150. agent_evolve/application/stratified_cold_start_allocation.py +732 -0
  151. agent_evolve/application/support_guarded_hurdle_score.py +549 -0
  152. agent_evolve/application/target_conditioned_action_forecast.py +595 -0
  153. agent_evolve/application/target_conditioned_campaign.py +566 -0
  154. agent_evolve/application/treatment_assignment.py +201 -0
  155. agent_evolve/application/trusted_objective_evidence.py +217 -0
  156. agent_evolve/application/two_stage_action_evolution.py +1131 -0
  157. agent_evolve/application/v8lite_allocation_policy.py +1083 -0
  158. agent_evolve/application/v9_candidate_policy.py +1303 -0
  159. agent_evolve/bootstrap.py +108 -0
  160. agent_evolve/campaign_presets.py +517 -0
  161. agent_evolve/campaign_profiles.py +452 -0
  162. agent_evolve/campaign_variation_topology.py +288 -0
  163. agent_evolve/campaign_workload.py +950 -0
  164. agent_evolve/cli.py +797 -0
  165. agent_evolve/contract.py +241 -0
  166. agent_evolve/core/__init__.py +91 -0
  167. agent_evolve/core/action_semantics.py +411 -0
  168. agent_evolve/core/authored.py +105 -0
  169. agent_evolve/core/formatting.py +286 -0
  170. agent_evolve/core/optimization_semantics.py +324 -0
  171. agent_evolve/core/problem.py +167 -0
  172. agent_evolve/core/results.py +323 -0
  173. agent_evolve/core/stats.py +70 -0
  174. agent_evolve/core/telemetry.py +100 -0
  175. agent_evolve/domain/__init__.py +89 -0
  176. agent_evolve/domain/artifact.py +162 -0
  177. agent_evolve/domain/durable_text.py +68 -0
  178. agent_evolve/domain/event.py +1454 -0
  179. agent_evolve/domain/finite_action_set.py +426 -0
  180. agent_evolve/domain/finite_variation.py +526 -0
  181. agent_evolve/domain/generative_emission.py +559 -0
  182. agent_evolve/domain/ids.py +163 -0
  183. agent_evolve/domain/inline_text.py +106 -0
  184. agent_evolve/domain/insight.py +27 -0
  185. agent_evolve/domain/lineage.py +737 -0
  186. agent_evolve/domain/llm_task_queue.py +960 -0
  187. agent_evolve/domain/outcome.py +96 -0
  188. agent_evolve/domain/patch.py +854 -0
  189. agent_evolve/domain/typed_json.py +542 -0
  190. agent_evolve/domain/variation_space.py +158 -0
  191. agent_evolve/driver.py +1014 -0
  192. agent_evolve/harness/__init__.py +29 -0
  193. agent_evolve/harness/base.py +242 -0
  194. agent_evolve/harness/directives.py +163 -0
  195. agent_evolve/harness/generative_seal.py +479 -0
  196. agent_evolve/harness/registry.py +41 -0
  197. agent_evolve/infrastructure/__init__.py +39 -0
  198. agent_evolve/infrastructure/artifacts/__init__.py +6 -0
  199. agent_evolve/infrastructure/artifacts/_verification.py +67 -0
  200. agent_evolve/infrastructure/artifacts/filesystem.py +343 -0
  201. agent_evolve/infrastructure/artifacts/in_memory.py +73 -0
  202. agent_evolve/infrastructure/asyncio_runtime.py +109 -0
  203. agent_evolve/infrastructure/authored_runtime.py +188 -0
  204. agent_evolve/infrastructure/authored_worker.py +171 -0
  205. agent_evolve/infrastructure/clock.py +53 -0
  206. agent_evolve/infrastructure/events/__init__.py +6 -0
  207. agent_evolve/infrastructure/events/_validation.py +89 -0
  208. agent_evolve/infrastructure/events/in_memory.py +56 -0
  209. agent_evolve/infrastructure/events/jsonl.py +193 -0
  210. agent_evolve/infrastructure/exception_provenance.py +215 -0
  211. agent_evolve/infrastructure/ids.py +118 -0
  212. agent_evolve/infrastructure/lineage_codec.py +1836 -0
  213. agent_evolve/infrastructure/outcome_adaptive_phase_journal.py +170 -0
  214. agent_evolve/infrastructure/residual_headroom_journal.py +221 -0
  215. agent_evolve/infrastructure/resource_lease.py +370 -0
  216. agent_evolve/infrastructure/sanitization/__init__.py +8 -0
  217. agent_evolve/infrastructure/sanitization/strict_json.py +484 -0
  218. agent_evolve/infrastructure/sequential_phase_journal.py +170 -0
  219. agent_evolve/infrastructure/stream_liveness.py +383 -0
  220. agent_evolve/infrastructure/subprocess_boundary.py +136 -0
  221. agent_evolve/integrations/__init__.py +1 -0
  222. agent_evolve/integrations/botorch/__init__.py +28 -0
  223. agent_evolve/integrations/botorch/finite_qlognehvi.py +190 -0
  224. agent_evolve/integrations/botorch/finite_qlognehvi_batch.py +155 -0
  225. agent_evolve/integrations/botorch/finite_qlognehvi_batch_identity.py +20 -0
  226. agent_evolve/integrations/botorch/finite_qlognehvi_batch_worker.py +55 -0
  227. agent_evolve/integrations/botorch/finite_qlognehvi_identity.py +22 -0
  228. agent_evolve/integrations/botorch/finite_qlognehvi_worker.py +55 -0
  229. agent_evolve/integrations/botorch/subprocess_qlognehvi.py +261 -0
  230. agent_evolve/integrations/botorch/subprocess_qlognehvi_batch.py +273 -0
  231. agent_evolve/integrations/completion.py +242 -0
  232. agent_evolve/integrations/pydantic_ai/__init__.py +441 -0
  233. agent_evolve/integrations/pydantic_ai/action_forecast.py +1068 -0
  234. agent_evolve/integrations/pydantic_ai/agentic_generator.py +2308 -0
  235. agent_evolve/integrations/pydantic_ai/async_generator.py +1604 -0
  236. agent_evolve/integrations/pydantic_ai/boundary_codec.py +1526 -0
  237. agent_evolve/integrations/pydantic_ai/calibrated_portfolio_campaign.py +756 -0
  238. agent_evolve/integrations/pydantic_ai/calibrated_portfolio_selection.py +7537 -0
  239. agent_evolve/integrations/pydantic_ai/campaign_acquisition.py +609 -0
  240. agent_evolve/integrations/pydantic_ai/execution_binding.py +138 -0
  241. agent_evolve/integrations/pydantic_ai/forecast_geometry_action_committee.py +217 -0
  242. agent_evolve/integrations/pydantic_ai/harness.py +159 -0
  243. agent_evolve/integrations/pydantic_ai/heterogeneous_model_execution.py +306 -0
  244. agent_evolve/integrations/pydantic_ai/hierarchical_residual_adaptive_semantic_view.py +179 -0
  245. agent_evolve/integrations/pydantic_ai/json_schema_dialect.py +108 -0
  246. agent_evolve/integrations/pydantic_ai/materialized_hierarchical_residual_expert.py +952 -0
  247. agent_evolve/integrations/pydantic_ai/materialized_portfolio_judge.py +520 -0
  248. agent_evolve/integrations/pydantic_ai/model_execution_profile.py +659 -0
  249. agent_evolve/integrations/pydantic_ai/outbound_request_manifest.py +1170 -0
  250. agent_evolve/integrations/pydantic_ai/portable_residual_consequence_features.py +575 -0
  251. agent_evolve/integrations/pydantic_ai/portfolio_selection.py +422 -0
  252. agent_evolve/integrations/pydantic_ai/progress_aware_openrouter.py +416 -0
  253. agent_evolve/integrations/pydantic_ai/provider_attempt_join.py +1523 -0
  254. agent_evolve/integrations/pydantic_ai/provider_free_calibrated_runner.py +607 -0
  255. agent_evolve/integrations/pydantic_ai/queued_runner.py +2634 -0
  256. agent_evolve/integrations/pydantic_ai/reconciled_residual_reachability.py +1417 -0
  257. agent_evolve/integrations/pydantic_ai/residual_forecast_geometry.py +445 -0
  258. agent_evolve/integrations/pydantic_ai/residual_reachability.py +674 -0
  259. agent_evolve/integrations/pydantic_ai/residual_semantic_cells.py +239 -0
  260. agent_evolve/integrations/pydantic_ai/sealed_output_replay.py +1068 -0
  261. agent_evolve/integrations/pydantic_ai/semantic_coverage_residual_portfolio.py +770 -0
  262. agent_evolve/integrations/pydantic_ai/semantic_decision_replay.py +383 -0
  263. agent_evolve/integrations/pydantic_ai/support_adaptive_residual_portfolio.py +135 -0
  264. agent_evolve/integrations/pydantic_ai/trusted_residual_prompt_context.py +143 -0
  265. agent_evolve/integrations/pydantic_ai/validated_openrouter_model.py +107 -0
  266. agent_evolve/integrations/pymoo_adapter.py +242 -0
  267. agent_evolve/policies/__init__.py +17 -0
  268. agent_evolve/policies/check.py +469 -0
  269. agent_evolve/policies/emit_scaffold.py +451 -0
  270. agent_evolve/policies/feedback/__init__.py +37 -0
  271. agent_evolve/policies/feedback/held_out_asn.py +1325 -0
  272. agent_evolve/policies/genetic.py +607 -0
  273. agent_evolve/policies/llm_backoff.py +183 -0
  274. agent_evolve/policies/llm_chooser.py +226 -0
  275. agent_evolve/policies/llm_generator.py +1760 -0
  276. agent_evolve/policies/llm_init.py +267 -0
  277. agent_evolve/policies/llm_operator.py +109 -0
  278. agent_evolve/policies/llm_prior.py +194 -0
  279. agent_evolve/policies/llm_surrogate.py +334 -0
  280. agent_evolve/policies/measurement_evidence.py +704 -0
  281. agent_evolve/policies/memory/__init__.py +223 -0
  282. agent_evolve/policies/memory/balanced_subset_blocks.py +707 -0
  283. agent_evolve/policies/memory/compatibility_matching.py +593 -0
  284. agent_evolve/policies/memory/global_falsification.py +1841 -0
  285. agent_evolve/policies/memory/prompt_shape.py +503 -0
  286. agent_evolve/policies/memory/randomized_subset.py +714 -0
  287. agent_evolve/policies/memory/staged_causal.py +1270 -0
  288. agent_evolve/policies/memory/treatment_compliance.py +759 -0
  289. agent_evolve/policies/objective_resolution/__init__.py +17 -0
  290. agent_evolve/policies/objective_resolution/fixed_grid.py +364 -0
  291. agent_evolve/policies/operator_portfolio.py +407 -0
  292. agent_evolve/policies/reguidance.py +1133 -0
  293. agent_evolve/policies/reward/__init__.py +83 -0
  294. agent_evolve/policies/reward/affine_candidate_consequence.py +156 -0
  295. agent_evolve/policies/reward/affine_candidate_consequence_3d.py +159 -0
  296. agent_evolve/policies/reward/affine_hypervolume.py +490 -0
  297. agent_evolve/policies/reward/affine_hypervolume_3d.py +567 -0
  298. agent_evolve/policies/reward/contextual_marginal_utility.py +318 -0
  299. agent_evolve/policies/reward/frozen_archive.py +360 -0
  300. agent_evolve/policies/reward/frozen_wave_archive.py +368 -0
  301. agent_evolve/policies/search_state.py +208 -0
  302. agent_evolve/policies/selection/__init__.py +345 -0
  303. agent_evolve/policies/selection/acquisition_certified_slate.py +684 -0
  304. agent_evolve/policies/selection/affine_frontier_context.py +330 -0
  305. agent_evolve/policies/selection/affine_frontier_target.py +473 -0
  306. agent_evolve/policies/selection/archive_elite.py +1346 -0
  307. agent_evolve/policies/selection/calibrated_portfolio_binding.py +640 -0
  308. agent_evolve/policies/selection/calibrated_slate.py +1394 -0
  309. agent_evolve/policies/selection/calibrated_slate_codec.py +579 -0
  310. agent_evolve/policies/selection/common_candidate_pool.py +685 -0
  311. agent_evolve/policies/selection/diagnostic_sampling.py +319 -0
  312. agent_evolve/policies/selection/disjoint_pairs.py +479 -0
  313. agent_evolve/policies/selection/elite_explorer.py +719 -0
  314. agent_evolve/policies/selection/finite_action.py +187 -0
  315. agent_evolve/policies/selection/finite_option_prompt_projection.py +377 -0
  316. agent_evolve/policies/selection/finite_palette_evidence.py +247 -0
  317. agent_evolve/policies/selection/forecast_calibration.py +922 -0
  318. agent_evolve/policies/selection/frontier_probe_slate.py +814 -0
  319. agent_evolve/policies/selection/frozen_archive_pairs.py +762 -0
  320. agent_evolve/policies/selection/full_support_slate.py +91 -0
  321. agent_evolve/policies/selection/meaningful_direction.py +240 -0
  322. agent_evolve/policies/selection/memory_dose_feasibility.py +259 -0
  323. agent_evolve/policies/selection/model_anchored_slate.py +826 -0
  324. agent_evolve/policies/selection/phenotype_recourse.py +979 -0
  325. agent_evolve/policies/selection/proposal_support.py +368 -0
  326. agent_evolve/policies/selection/random_portfolio.py +254 -0
  327. agent_evolve/policies/selection/regret_bounded_slate.py +1084 -0
  328. agent_evolve/policies/selection/residual_frontier.py +463 -0
  329. agent_evolve/policies/selection/residual_frontier_target.py +605 -0
  330. agent_evolve/policies/selection/structural_posterior_slate.py +1571 -0
  331. agent_evolve/policies/selection/target_conditioned_allocator.py +648 -0
  332. agent_evolve/policies/selection/target_conditioned_features.py +812 -0
  333. agent_evolve/policies/selection/target_conditioned_prequential.py +1527 -0
  334. agent_evolve/policies/selection/task_keyed_palette.py +906 -0
  335. agent_evolve/policies/semantics.py +147 -0
  336. agent_evolve/policies/structure.py +362 -0
  337. agent_evolve/policies/structured_output_budget.py +62 -0
  338. agent_evolve/policies/surrogate.py +696 -0
  339. agent_evolve/policies/variation/__init__.py +1 -0
  340. agent_evolve/policies/variation/compositional_finite_catalog.py +426 -0
  341. agent_evolve/policies/variation/crossover_inheritance.py +575 -0
  342. agent_evolve/policies/variation/disjoint_recombination.py +611 -0
  343. agent_evolve/policies/variation/exact_composition_capacity.py +214 -0
  344. agent_evolve/policies/variation/exact_parent_crossover.py +950 -0
  345. agent_evolve/policies/variation/multiscale_restart_catalog.py +372 -0
  346. agent_evolve/policies/variation/source_union_finite_catalog.py +403 -0
  347. agent_evolve/policies/variation/typed_patch.py +1981 -0
  348. agent_evolve/policies/weighted_prior.py +394 -0
  349. agent_evolve/ports/__init__.py +383 -0
  350. agent_evolve/ports/action_allocation.py +733 -0
  351. agent_evolve/ports/action_allocation_frame.py +1153 -0
  352. agent_evolve/ports/action_allocation_frame_commit.py +294 -0
  353. agent_evolve/ports/action_allocation_frame_commit_v3.py +432 -0
  354. agent_evolve/ports/action_allocation_frame_v3.py +995 -0
  355. agent_evolve/ports/action_forecast.py +1568 -0
  356. agent_evolve/ports/action_metric_projection.py +165 -0
  357. agent_evolve/ports/agentic_generator.py +1561 -0
  358. agent_evolve/ports/archive_context.py +136 -0
  359. agent_evolve/ports/artifact_sanitizer.py +44 -0
  360. agent_evolve/ports/artifact_store.py +225 -0
  361. agent_evolve/ports/clock.py +13 -0
  362. agent_evolve/ports/contextual_search_allocation.py +827 -0
  363. agent_evolve/ports/decision_metric_projection.py +258 -0
  364. agent_evolve/ports/event_store.py +55 -0
  365. agent_evolve/ports/executable_hypothesis.py +557 -0
  366. agent_evolve/ports/finite_acquisition.py +377 -0
  367. agent_evolve/ports/finite_acquisition_batch.py +296 -0
  368. agent_evolve/ports/finite_acquisition_batch_json.py +164 -0
  369. agent_evolve/ports/finite_acquisition_json.py +247 -0
  370. agent_evolve/ports/finite_acquisition_space.py +168 -0
  371. agent_evolve/ports/finite_action_selection.py +348 -0
  372. agent_evolve/ports/finite_action_set.py +256 -0
  373. agent_evolve/ports/frontier_target.py +396 -0
  374. agent_evolve/ports/generation_failure.py +43 -0
  375. agent_evolve/ports/hard_feasibility.py +233 -0
  376. agent_evolve/ports/id_factory.py +34 -0
  377. agent_evolve/ports/llm_task_queue.py +93 -0
  378. agent_evolve/ports/objective_resolution.py +419 -0
  379. agent_evolve/ports/paired_allocation_comparison.py +401 -0
  380. agent_evolve/ports/paired_block_schedule.py +475 -0
  381. agent_evolve/ports/parent_measurement.py +336 -0
  382. agent_evolve/ports/portfolio_memory_dose.py +643 -0
  383. agent_evolve/ports/portfolio_selection.py +3169 -0
  384. agent_evolve/ports/postcommit_rank_authority.py +467 -0
  385. agent_evolve/ports/presented_action_evidence.py +794 -0
  386. agent_evolve/ports/resource_lease.py +162 -0
  387. agent_evolve/ports/structured_generator.py +734 -0
  388. agent_evolve/ports/structured_output_budget.py +120 -0
  389. agent_evolve/ports/subprocess_boundary.py +138 -0
  390. agent_evolve/ports/treatment_assignment.py +466 -0
  391. agent_evolve/ports/variation_catalog.py +76 -0
  392. agent_evolve/ports/variation_source.py +226 -0
  393. agent_evolve/proposal_mode.py +157 -0
  394. agent_evolve/proposers/__init__.py +10 -0
  395. agent_evolve/proposers/random_proposer.py +188 -0
  396. agent_evolve/provider_accounting.py +163 -0
  397. agent_evolve/py.typed +0 -0
  398. agent_evolve/reference_method.py +1570 -0
  399. agent_evolve/session/__init__.py +11 -0
  400. agent_evolve/session/authorship.py +864 -0
  401. agent_evolve/session/evaluate.py +236 -0
  402. agent_evolve/session/fidelity.py +237 -0
  403. agent_evolve/session/genetic_loop.py +742 -0
  404. agent_evolve/session/loop.py +803 -0
  405. agent_evolve/session/screening.py +671 -0
  406. agent_evolve/settings.py +376 -0
  407. agent_evolve/workload_kit.py +368 -0
  408. agent_evolve/workload_prompt.py +398 -0
  409. agentevolve_optimizer-0.5.0.dist-info/METADATA +599 -0
  410. agentevolve_optimizer-0.5.0.dist-info/RECORD +414 -0
  411. agentevolve_optimizer-0.5.0.dist-info/WHEEL +5 -0
  412. agentevolve_optimizer-0.5.0.dist-info/entry_points.txt +2 -0
  413. agentevolve_optimizer-0.5.0.dist-info/licenses/LICENSE +21 -0
  414. agentevolve_optimizer-0.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,979 @@
1
+ """Objective-blind phenotype identity and bounded evaluation recourse.
2
+
3
+ Candidate occurrences are causal units: two independently assigned trials remain
4
+ distinct even when they materialize the same evaluation phenotype. Physical
5
+ evaluation identity is a separate, injected policy. This module joins those two
6
+ views without importing an evaluator, reward, archive, model provider, engine, or
7
+ optimizer.
8
+
9
+ Recourse is deliberately narrow. A pool is sealed before primary outcomes are
10
+ known, only successful *primary* collisions create credit, and successful
11
+ recourse outcomes can never create more credit. Infrastructure/system failures
12
+ in the primary wave invalidate the block; model and candidate failures are valid
13
+ zero-yield causal outcomes. The deterministic slot cap is::
14
+
15
+ min(max_recourse,
16
+ successful_primary_collision_credit,
17
+ unique_budget_after_reservations_and_recombination_protection,
18
+ eligible_presealed_pool_size)
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import hashlib
24
+ import json
25
+ import re
26
+ from collections.abc import Callable, Sequence
27
+ from dataclasses import dataclass
28
+ from enum import Enum
29
+ from itertools import combinations
30
+ from typing import ClassVar, Protocol
31
+
32
+ from agent_evolve.domain.ids import CandidateId, OperatorInvocationId
33
+ from agent_evolve.domain.patch import require_sha256
34
+ from agent_evolve.domain.typed_json import (
35
+ FrozenJsonValue,
36
+ freeze_json,
37
+ is_frozen_json_value,
38
+ typed_json_sha256,
39
+ )
40
+
41
+
42
+ IDENTITY_POLICY_ID = "typed_configuration_phenotype"
43
+ IDENTITY_POLICY_VERSION = 1
44
+ RECOURSE_POLICY_ID = "bounded_collision_evaluation_recourse"
45
+ RECOURSE_POLICY_VERSION = 1
46
+
47
+ _IDENTITY_DOMAIN = b"agent-evolve:phenotype-identity:v1\x00"
48
+ _LEDGER_DOMAIN = b"agent-evolve:phenotype-occurrence-ledger:v1\x00"
49
+ _POOL_DOMAIN = b"agent-evolve:presealed-recourse-pool:v1\x00"
50
+ _DECISION_DOMAIN = b"agent-evolve:evaluation-recourse-decision:v1\x00"
51
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,127}$")
52
+
53
+
54
+ def _token(value: str, *, name: str) -> None:
55
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
56
+ raise ValueError(f"{name} must use the closed lowercase token grammar")
57
+
58
+
59
+ def _positive(value: int, *, name: str) -> None:
60
+ if type(value) is not int or value <= 0:
61
+ raise ValueError(f"{name} must be a positive integer")
62
+
63
+
64
+ def _nonnegative(value: int, *, name: str) -> None:
65
+ if type(value) is not int or value < 0:
66
+ raise ValueError(f"{name} must be a non-negative integer")
67
+
68
+
69
+ def _canonical_json(value: object) -> bytes:
70
+ return json.dumps(
71
+ value,
72
+ allow_nan=False,
73
+ ensure_ascii=True,
74
+ separators=(",", ":"),
75
+ sort_keys=True,
76
+ ).encode("ascii")
77
+
78
+
79
+ def _domain_hash(domain: bytes, value: object) -> str:
80
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
81
+
82
+
83
+ @dataclass(frozen=True, slots=True)
84
+ class PhenotypeIdentity:
85
+ """A policy-bound digest used to coalesce physical evaluations."""
86
+
87
+ policy_id: str
88
+ policy_version: int
89
+ value_sha256: str
90
+
91
+ def __post_init__(self) -> None:
92
+ _token(self.policy_id, name="identity policy_id")
93
+ _positive(self.policy_version, name="identity policy_version")
94
+ require_sha256(self.value_sha256, "phenotype value_sha256")
95
+
96
+ @property
97
+ def identity_sha256(self) -> str:
98
+ return _domain_hash(_IDENTITY_DOMAIN, self.to_trace_record())
99
+
100
+ def to_trace_record(self) -> dict[str, object]:
101
+ return {
102
+ "policy_id": self.policy_id,
103
+ "policy_version": self.policy_version,
104
+ "value_sha256": self.value_sha256,
105
+ }
106
+
107
+
108
+ class PhenotypeIdentityPolicy(Protocol):
109
+ """Injected boundary between a typed configuration and evaluator identity."""
110
+
111
+ policy_id: str
112
+ policy_version: int
113
+
114
+ def identify(self, configuration: object) -> PhenotypeIdentity: ...
115
+
116
+
117
+ @dataclass(frozen=True, slots=True)
118
+ class TypedConfigurationPhenotypeIdentityPolicy:
119
+ """Default identity: the exact, type-sensitive configuration tree."""
120
+
121
+ policy_id: ClassVar[str] = IDENTITY_POLICY_ID
122
+ policy_version: ClassVar[int] = IDENTITY_POLICY_VERSION
123
+
124
+ def identify(self, configuration: object) -> PhenotypeIdentity:
125
+ return PhenotypeIdentity(
126
+ policy_id=self.policy_id,
127
+ policy_version=self.policy_version,
128
+ value_sha256=typed_json_sha256(freeze_json(configuration)),
129
+ )
130
+
131
+
132
+ SemanticProjection = Callable[[FrozenJsonValue], object]
133
+
134
+
135
+ @dataclass(frozen=True, slots=True)
136
+ class SemanticProjectionPhenotypeIdentityPolicy:
137
+ """Hash an injected deterministic semantic projection of a configuration.
138
+
139
+ The projector receives an immutable typed-JSON tree and must return another
140
+ typed-JSON value. A problem adapter can therefore erase evaluator-inert
141
+ syntax (aliases, ordering, decoded no-ops) without weakening the default
142
+ type-sensitive identity used by domains that have no such equivalence.
143
+ """
144
+
145
+ policy_id: str
146
+ policy_version: int
147
+ projector: SemanticProjection
148
+
149
+ def __post_init__(self) -> None:
150
+ _token(self.policy_id, name="semantic identity policy_id")
151
+ _positive(self.policy_version, name="semantic identity policy_version")
152
+ if not callable(self.projector):
153
+ raise TypeError("projector must be callable")
154
+
155
+ def identify(self, configuration: object) -> PhenotypeIdentity:
156
+ immutable_configuration = freeze_json(configuration)
157
+ projected = freeze_json(self.projector(immutable_configuration))
158
+ return PhenotypeIdentity(
159
+ policy_id=self.policy_id,
160
+ policy_version=self.policy_version,
161
+ value_sha256=typed_json_sha256(projected),
162
+ )
163
+
164
+
165
+ class EvaluationOccurrenceRole(str, Enum):
166
+ PRIMARY = "primary"
167
+ RECOURSE = "recourse"
168
+
169
+
170
+ class EvaluationOccurrenceStatus(str, Enum):
171
+ """Terminal status with explicit causal and block-validity semantics."""
172
+
173
+ SUCCESS = "success"
174
+ CANDIDATE_FAILURE = "candidate_failure"
175
+ MODEL_FAILURE = "model_failure"
176
+ INFRASTRUCTURE_FAILURE = "infrastructure_failure"
177
+ SYSTEM_FAILURE = "system_failure"
178
+
179
+ @property
180
+ def invalidates_primary_block(self) -> bool:
181
+ return self in (
182
+ EvaluationOccurrenceStatus.INFRASTRUCTURE_FAILURE,
183
+ EvaluationOccurrenceStatus.SYSTEM_FAILURE,
184
+ )
185
+
186
+
187
+ @dataclass(frozen=True, slots=True)
188
+ class PhenotypeOccurrence:
189
+ """One causal trial occurrence, independent of physical cache identity."""
190
+
191
+ trial_id: OperatorInvocationId
192
+ role: EvaluationOccurrenceRole
193
+ status: EvaluationOccurrenceStatus
194
+ candidate_id: CandidateId | None
195
+ phenotype: PhenotypeIdentity | None
196
+
197
+ def __post_init__(self) -> None:
198
+ if type(self.trial_id) is not OperatorInvocationId:
199
+ raise TypeError("trial_id must be an exact OperatorInvocationId")
200
+ OperatorInvocationId.__post_init__(self.trial_id)
201
+ if type(self.role) is not EvaluationOccurrenceRole:
202
+ raise TypeError("role must be an EvaluationOccurrenceRole")
203
+ if type(self.status) is not EvaluationOccurrenceStatus:
204
+ raise TypeError("status must be an EvaluationOccurrenceStatus")
205
+ if self.candidate_id is not None:
206
+ if type(self.candidate_id) is not CandidateId:
207
+ raise TypeError("candidate_id must be an exact CandidateId or None")
208
+ CandidateId.__post_init__(self.candidate_id)
209
+ if self.phenotype is not None:
210
+ if type(self.phenotype) is not PhenotypeIdentity:
211
+ raise TypeError("phenotype must be an exact PhenotypeIdentity or None")
212
+ PhenotypeIdentity.__post_init__(self.phenotype)
213
+
214
+ has_candidate = self.candidate_id is not None
215
+ has_phenotype = self.phenotype is not None
216
+ if has_candidate != has_phenotype:
217
+ raise ValueError("candidate_id and phenotype must be present together")
218
+ if self.status is EvaluationOccurrenceStatus.SUCCESS and not has_candidate:
219
+ raise ValueError("success requires candidate and phenotype")
220
+ if self.status is EvaluationOccurrenceStatus.MODEL_FAILURE and has_candidate:
221
+ raise ValueError("model_failure cannot carry candidate or phenotype")
222
+ if (
223
+ self.role is EvaluationOccurrenceRole.RECOURSE
224
+ and self.status is EvaluationOccurrenceStatus.MODEL_FAILURE
225
+ ):
226
+ raise ValueError("engine-owned recourse cannot have model_failure status")
227
+
228
+ def to_trace_record(self) -> dict[str, object]:
229
+ return {
230
+ "trial_id": self.trial_id.value,
231
+ "role": self.role.value,
232
+ "status": self.status.value,
233
+ "candidate_id": (
234
+ None if self.candidate_id is None else self.candidate_id.value
235
+ ),
236
+ "phenotype_identity_sha256": (
237
+ None if self.phenotype is None else self.phenotype.identity_sha256
238
+ ),
239
+ }
240
+
241
+
242
+ def _occurrence_key(occurrence: PhenotypeOccurrence) -> str:
243
+ return occurrence.trial_id.value
244
+
245
+
246
+ @dataclass(frozen=True, slots=True)
247
+ class PhenotypeCluster:
248
+ """All causal occurrences sharing one physical evaluation phenotype."""
249
+
250
+ phenotype: PhenotypeIdentity
251
+ occurrences: tuple[PhenotypeOccurrence, ...]
252
+
253
+ def __post_init__(self) -> None:
254
+ if type(self.phenotype) is not PhenotypeIdentity:
255
+ raise TypeError("phenotype must be an exact PhenotypeIdentity")
256
+ PhenotypeIdentity.__post_init__(self.phenotype)
257
+ if type(self.occurrences) is not tuple or not self.occurrences:
258
+ raise ValueError("cluster occurrences must be a non-empty exact tuple")
259
+ if any(type(item) is not PhenotypeOccurrence for item in self.occurrences):
260
+ raise TypeError("cluster must contain exact PhenotypeOccurrence values")
261
+ for occurrence in self.occurrences:
262
+ PhenotypeOccurrence.__post_init__(occurrence)
263
+ if occurrence.phenotype != self.phenotype:
264
+ raise ValueError("cluster contains a different phenotype")
265
+ expected = tuple(sorted(self.occurrences, key=_occurrence_key))
266
+ if self.occurrences != expected:
267
+ raise ValueError("cluster occurrences must be canonically ordered")
268
+ if len({item.trial_id for item in self.occurrences}) != len(self.occurrences):
269
+ raise ValueError("cluster trial IDs must be unique")
270
+
271
+ @property
272
+ def successful_primary_occurrences(self) -> tuple[PhenotypeOccurrence, ...]:
273
+ return tuple(
274
+ item
275
+ for item in self.occurrences
276
+ if item.role is EvaluationOccurrenceRole.PRIMARY
277
+ and item.status is EvaluationOccurrenceStatus.SUCCESS
278
+ )
279
+
280
+ @property
281
+ def successful_primary_collision_credit(self) -> int:
282
+ return max(0, len(self.successful_primary_occurrences) - 1)
283
+
284
+ @property
285
+ def zero_identity_contrast_pairs(
286
+ self,
287
+ ) -> tuple[tuple[OperatorInvocationId, OperatorInvocationId], ...]:
288
+ """Successful primary pairs whose evaluation contrast is zero by identity."""
289
+
290
+ return tuple(
291
+ (left.trial_id, right.trial_id)
292
+ for left, right in combinations(self.successful_primary_occurrences, 2)
293
+ )
294
+
295
+ def to_trace_record(self) -> dict[str, object]:
296
+ return {
297
+ "phenotype": self.phenotype.to_trace_record(),
298
+ "phenotype_identity_sha256": self.phenotype.identity_sha256,
299
+ "occurrences": [item.to_trace_record() for item in self.occurrences],
300
+ "successful_primary_collision_credit": (
301
+ self.successful_primary_collision_credit
302
+ ),
303
+ "zero_identity_contrast_pairs": [
304
+ [left.value, right.value]
305
+ for left, right in self.zero_identity_contrast_pairs
306
+ ],
307
+ }
308
+
309
+
310
+ @dataclass(frozen=True, slots=True, eq=False)
311
+ class PhenotypeOccurrenceLedger:
312
+ """Canonical occurrence history and its phenotype clusters."""
313
+
314
+ identity_policy_id: str
315
+ identity_policy_version: int
316
+ occurrences: tuple[PhenotypeOccurrence, ...]
317
+
318
+ def __post_init__(self) -> None:
319
+ _token(self.identity_policy_id, name="ledger identity_policy_id")
320
+ _positive(
321
+ self.identity_policy_version,
322
+ name="ledger identity_policy_version",
323
+ )
324
+ if type(self.occurrences) is not tuple or any(
325
+ type(item) is not PhenotypeOccurrence for item in self.occurrences
326
+ ):
327
+ raise TypeError("occurrences must contain exact PhenotypeOccurrence values")
328
+ for occurrence in self.occurrences:
329
+ PhenotypeOccurrence.__post_init__(occurrence)
330
+ phenotype = occurrence.phenotype
331
+ if phenotype is not None and (
332
+ phenotype.policy_id != self.identity_policy_id
333
+ or phenotype.policy_version != self.identity_policy_version
334
+ ):
335
+ raise ValueError(
336
+ "occurrence phenotype uses a different identity policy"
337
+ )
338
+ expected = tuple(sorted(self.occurrences, key=_occurrence_key))
339
+ if self.occurrences != expected:
340
+ raise ValueError("occurrences must be canonically ordered by trial ID")
341
+ if len({item.trial_id for item in self.occurrences}) != len(self.occurrences):
342
+ raise ValueError("occurrence trial IDs must be unique")
343
+ candidate_ids = tuple(
344
+ item.candidate_id
345
+ for item in self.occurrences
346
+ if item.candidate_id is not None
347
+ )
348
+ if len(set(candidate_ids)) != len(candidate_ids):
349
+ raise ValueError("occurrence candidate IDs must be unique")
350
+
351
+ @classmethod
352
+ def build(
353
+ cls,
354
+ occurrences: Sequence[PhenotypeOccurrence],
355
+ *,
356
+ identity_policy: PhenotypeIdentityPolicy,
357
+ ) -> "PhenotypeOccurrenceLedger":
358
+ if isinstance(occurrences, (str, bytes)):
359
+ raise TypeError("occurrences must be a finite occurrence sequence")
360
+ if not callable(getattr(identity_policy, "identify", None)):
361
+ raise TypeError("identity_policy must implement identify")
362
+ policy_id = getattr(identity_policy, "policy_id", None)
363
+ policy_version = getattr(identity_policy, "policy_version", None)
364
+ _token(policy_id, name="identity policy_id")
365
+ _positive(policy_version, name="identity policy_version")
366
+ values = tuple(occurrences)
367
+ for value in values:
368
+ if type(value) is not PhenotypeOccurrence:
369
+ raise TypeError(
370
+ "occurrences must contain exact PhenotypeOccurrence values"
371
+ )
372
+ return cls(
373
+ identity_policy_id=policy_id,
374
+ identity_policy_version=policy_version,
375
+ occurrences=tuple(sorted(values, key=_occurrence_key)),
376
+ )
377
+
378
+ @property
379
+ def clusters(self) -> tuple[PhenotypeCluster, ...]:
380
+ grouped: dict[str, list[PhenotypeOccurrence]] = {}
381
+ identities: dict[str, PhenotypeIdentity] = {}
382
+ for occurrence in self.occurrences:
383
+ phenotype = occurrence.phenotype
384
+ if phenotype is None:
385
+ continue
386
+ key = phenotype.identity_sha256
387
+ identities[key] = phenotype
388
+ grouped.setdefault(key, []).append(occurrence)
389
+ return tuple(
390
+ PhenotypeCluster(identities[key], tuple(grouped[key]))
391
+ for key in sorted(grouped)
392
+ )
393
+
394
+ @property
395
+ def successful_primary_collision_credit(self) -> int:
396
+ return sum(
397
+ cluster.successful_primary_collision_credit for cluster in self.clusters
398
+ )
399
+
400
+ @property
401
+ def primary_occurrences(self) -> tuple[PhenotypeOccurrence, ...]:
402
+ return tuple(
403
+ item
404
+ for item in self.occurrences
405
+ if item.role is EvaluationOccurrenceRole.PRIMARY
406
+ )
407
+
408
+ @property
409
+ def primary_phenotypes(self) -> frozenset[PhenotypeIdentity]:
410
+ return frozenset(
411
+ item.phenotype
412
+ for item in self.primary_occurrences
413
+ if item.phenotype is not None
414
+ )
415
+
416
+ @property
417
+ def invalidating_primary_trial_ids(self) -> tuple[OperatorInvocationId, ...]:
418
+ return tuple(
419
+ item.trial_id
420
+ for item in self.primary_occurrences
421
+ if item.status.invalidates_primary_block
422
+ )
423
+
424
+ @property
425
+ def invalidating_trial_ids(self) -> tuple[OperatorInvocationId, ...]:
426
+ """Every infrastructure/system failure, including post-decision recourse."""
427
+
428
+ return tuple(
429
+ item.trial_id
430
+ for item in self.occurrences
431
+ if item.status.invalidates_primary_block
432
+ )
433
+
434
+ @property
435
+ def primary_block_valid(self) -> bool:
436
+ return not self.invalidating_primary_trial_ids
437
+
438
+ @property
439
+ def experiment_block_valid(self) -> bool:
440
+ """False if infrastructure/system failure occurred at any wave stage."""
441
+
442
+ return not self.invalidating_trial_ids
443
+
444
+ @property
445
+ def ignored_recourse_trial_ids(self) -> tuple[OperatorInvocationId, ...]:
446
+ return tuple(
447
+ item.trial_id
448
+ for item in self.occurrences
449
+ if item.role is EvaluationOccurrenceRole.RECOURSE
450
+ )
451
+
452
+ def is_zero_identity_contrast(
453
+ self,
454
+ left_trial_id: OperatorInvocationId,
455
+ right_trial_id: OperatorInvocationId,
456
+ ) -> bool:
457
+ """Whether two successful primary trials map to one phenotype."""
458
+
459
+ for value, name in (
460
+ (left_trial_id, "left_trial_id"),
461
+ (right_trial_id, "right_trial_id"),
462
+ ):
463
+ if type(value) is not OperatorInvocationId:
464
+ raise TypeError(f"{name} must be an exact OperatorInvocationId")
465
+ if left_trial_id == right_trial_id:
466
+ raise ValueError("contrast trial IDs must be distinct")
467
+ by_id = {item.trial_id: item for item in self.occurrences}
468
+ if left_trial_id not in by_id or right_trial_id not in by_id:
469
+ raise KeyError("contrast trial ID is absent from the ledger")
470
+ left = by_id[left_trial_id]
471
+ right = by_id[right_trial_id]
472
+ return (
473
+ left.role is EvaluationOccurrenceRole.PRIMARY
474
+ and right.role is EvaluationOccurrenceRole.PRIMARY
475
+ and left.status is EvaluationOccurrenceStatus.SUCCESS
476
+ and right.status is EvaluationOccurrenceStatus.SUCCESS
477
+ and left.phenotype is not None
478
+ and left.phenotype == right.phenotype
479
+ )
480
+
481
+ def _trace_payload(self) -> dict[str, object]:
482
+ return {
483
+ "identity_policy_id": self.identity_policy_id,
484
+ "identity_policy_version": self.identity_policy_version,
485
+ "occurrences": [item.to_trace_record() for item in self.occurrences],
486
+ "clusters": [cluster.to_trace_record() for cluster in self.clusters],
487
+ "successful_primary_collision_credit": (
488
+ self.successful_primary_collision_credit
489
+ ),
490
+ "invalidating_primary_trial_ids": [
491
+ item.value for item in self.invalidating_primary_trial_ids
492
+ ],
493
+ "invalidating_trial_ids": [
494
+ item.value for item in self.invalidating_trial_ids
495
+ ],
496
+ "primary_block_valid": self.primary_block_valid,
497
+ "experiment_block_valid": self.experiment_block_valid,
498
+ "ignored_recourse_trial_ids": [
499
+ item.value for item in self.ignored_recourse_trial_ids
500
+ ],
501
+ }
502
+
503
+ @property
504
+ def ledger_sha256(self) -> str:
505
+ return _domain_hash(_LEDGER_DOMAIN, self._trace_payload())
506
+
507
+ def to_trace_record(self) -> dict[str, object]:
508
+ return {**self._trace_payload(), "ledger_sha256": self.ledger_sha256}
509
+
510
+ def __eq__(self, other: object) -> bool:
511
+ return (
512
+ type(self) is PhenotypeOccurrenceLedger
513
+ and type(other) is PhenotypeOccurrenceLedger
514
+ and self.ledger_sha256 == other.ledger_sha256
515
+ )
516
+
517
+ __hash__ = None
518
+
519
+
520
+ @dataclass(frozen=True, slots=True)
521
+ class RecoursePoolCandidate:
522
+ """An engine-authored recourse candidate before identity materialization."""
523
+
524
+ entry_id: str
525
+ configuration: FrozenJsonValue
526
+
527
+ def __post_init__(self) -> None:
528
+ _token(self.entry_id, name="recourse entry_id")
529
+ if not is_frozen_json_value(self.configuration):
530
+ raise TypeError("recourse configuration must already be frozen typed JSON")
531
+ freeze_json(self.configuration)
532
+
533
+ @classmethod
534
+ def freeze(cls, entry_id: str, configuration: object) -> "RecoursePoolCandidate":
535
+ return cls(entry_id=entry_id, configuration=freeze_json(configuration))
536
+
537
+
538
+ @dataclass(frozen=True, slots=True)
539
+ class RecoursePoolEntry:
540
+ """One ordered, identity-bound candidate in a pre-outcome pool."""
541
+
542
+ ordinal: int
543
+ candidate: RecoursePoolCandidate
544
+ phenotype: PhenotypeIdentity
545
+
546
+ def __post_init__(self) -> None:
547
+ _nonnegative(self.ordinal, name="recourse entry ordinal")
548
+ if type(self.candidate) is not RecoursePoolCandidate:
549
+ raise TypeError("candidate must be an exact RecoursePoolCandidate")
550
+ RecoursePoolCandidate.__post_init__(self.candidate)
551
+ if type(self.phenotype) is not PhenotypeIdentity:
552
+ raise TypeError("phenotype must be an exact PhenotypeIdentity")
553
+ PhenotypeIdentity.__post_init__(self.phenotype)
554
+
555
+ @property
556
+ def entry_id(self) -> str:
557
+ return self.candidate.entry_id
558
+
559
+ def to_trace_record(self) -> dict[str, object]:
560
+ return {
561
+ "ordinal": self.ordinal,
562
+ "entry_id": self.entry_id,
563
+ "typed_configuration_sha256": typed_json_sha256(
564
+ self.candidate.configuration
565
+ ),
566
+ "phenotype_identity_sha256": self.phenotype.identity_sha256,
567
+ }
568
+
569
+
570
+ @dataclass(frozen=True, slots=True, eq=False)
571
+ class PresealedRecoursePool:
572
+ """An ordered recourse pool cryptographically bound before outcomes."""
573
+
574
+ pool_id: str
575
+ seal_context_sha256: str
576
+ identity_policy_id: str
577
+ identity_policy_version: int
578
+ entries: tuple[RecoursePoolEntry, ...]
579
+
580
+ def __post_init__(self) -> None:
581
+ _token(self.pool_id, name="recourse pool_id")
582
+ require_sha256(self.seal_context_sha256, "seal_context_sha256")
583
+ _token(self.identity_policy_id, name="pool identity_policy_id")
584
+ _positive(
585
+ self.identity_policy_version,
586
+ name="pool identity_policy_version",
587
+ )
588
+ if type(self.entries) is not tuple or any(
589
+ type(item) is not RecoursePoolEntry for item in self.entries
590
+ ):
591
+ raise TypeError("entries must contain exact RecoursePoolEntry values")
592
+ for entry in self.entries:
593
+ RecoursePoolEntry.__post_init__(entry)
594
+ if (
595
+ entry.phenotype.policy_id != self.identity_policy_id
596
+ or entry.phenotype.policy_version != self.identity_policy_version
597
+ ):
598
+ raise ValueError("pool entry uses a different identity policy")
599
+ if tuple(entry.ordinal for entry in self.entries) != tuple(
600
+ range(len(self.entries))
601
+ ):
602
+ raise ValueError("pool ordinals must be contiguous and ordered from zero")
603
+ if len({entry.entry_id for entry in self.entries}) != len(self.entries):
604
+ raise ValueError("recourse pool entry IDs must be unique")
605
+ identities = tuple(entry.phenotype for entry in self.entries)
606
+ if len(set(identities)) != len(identities):
607
+ raise ValueError("recourse pool phenotypes must be unique")
608
+
609
+ @classmethod
610
+ def seal(
611
+ cls,
612
+ *,
613
+ pool_id: str,
614
+ seal_context_sha256: str,
615
+ candidates: Sequence[RecoursePoolCandidate],
616
+ identity_policy: PhenotypeIdentityPolicy,
617
+ ) -> "PresealedRecoursePool":
618
+ if isinstance(candidates, (str, bytes)):
619
+ raise TypeError("candidates must be a finite candidate sequence")
620
+ if not callable(getattr(identity_policy, "identify", None)):
621
+ raise TypeError("identity_policy must implement identify")
622
+ policy_id = getattr(identity_policy, "policy_id", None)
623
+ policy_version = getattr(identity_policy, "policy_version", None)
624
+ _token(policy_id, name="identity policy_id")
625
+ _positive(policy_version, name="identity policy_version")
626
+ values = tuple(candidates)
627
+ entries: list[RecoursePoolEntry] = []
628
+ for ordinal, candidate in enumerate(values):
629
+ if type(candidate) is not RecoursePoolCandidate:
630
+ raise TypeError(
631
+ "candidates must contain exact RecoursePoolCandidate values"
632
+ )
633
+ identity = identity_policy.identify(candidate.configuration)
634
+ if type(identity) is not PhenotypeIdentity:
635
+ raise TypeError("identity policy must return exact PhenotypeIdentity")
636
+ if (
637
+ identity.policy_id != policy_id
638
+ or identity.policy_version != policy_version
639
+ ):
640
+ raise ValueError(
641
+ "identity policy returned inconsistent policy metadata"
642
+ )
643
+ entries.append(RecoursePoolEntry(ordinal, candidate, identity))
644
+ return cls(
645
+ pool_id=pool_id,
646
+ seal_context_sha256=seal_context_sha256,
647
+ identity_policy_id=policy_id,
648
+ identity_policy_version=policy_version,
649
+ entries=tuple(entries),
650
+ )
651
+
652
+ def _trace_payload(self) -> dict[str, object]:
653
+ return {
654
+ "pool_id": self.pool_id,
655
+ "seal_context_sha256": self.seal_context_sha256,
656
+ "identity_policy_id": self.identity_policy_id,
657
+ "identity_policy_version": self.identity_policy_version,
658
+ "entries": [entry.to_trace_record() for entry in self.entries],
659
+ }
660
+
661
+ @property
662
+ def pool_sha256(self) -> str:
663
+ return _domain_hash(_POOL_DOMAIN, self._trace_payload())
664
+
665
+ def to_trace_record(self) -> dict[str, object]:
666
+ return {**self._trace_payload(), "pool_sha256": self.pool_sha256}
667
+
668
+ def __eq__(self, other: object) -> bool:
669
+ return (
670
+ type(self) is PresealedRecoursePool
671
+ and type(other) is PresealedRecoursePool
672
+ and self.pool_sha256 == other.pool_sha256
673
+ )
674
+
675
+ __hash__ = None
676
+
677
+
678
+ @dataclass(frozen=True, slots=True)
679
+ class RecourseBudgetSnapshot:
680
+ """Unique-evaluation facts visible to the objective-blind recourse policy."""
681
+
682
+ max_unique_evaluations: int
683
+ used_unique_evaluations: int
684
+ reserved_non_recourse_evaluations: int
685
+ protected_recombination_evaluations: int
686
+
687
+ def __post_init__(self) -> None:
688
+ _nonnegative(self.max_unique_evaluations, name="max_unique_evaluations")
689
+ _nonnegative(self.used_unique_evaluations, name="used_unique_evaluations")
690
+ _nonnegative(
691
+ self.reserved_non_recourse_evaluations,
692
+ name="reserved_non_recourse_evaluations",
693
+ )
694
+ _nonnegative(
695
+ self.protected_recombination_evaluations,
696
+ name="protected_recombination_evaluations",
697
+ )
698
+ if self.used_unique_evaluations > self.max_unique_evaluations:
699
+ raise ValueError("used_unique_evaluations exceeds the hard maximum")
700
+ if (
701
+ self.used_unique_evaluations
702
+ + self.reserved_non_recourse_evaluations
703
+ + self.protected_recombination_evaluations
704
+ > self.max_unique_evaluations
705
+ ):
706
+ raise ValueError(
707
+ "used, reserved, and protected evaluations exceed the hard maximum"
708
+ )
709
+
710
+ @property
711
+ def available_for_recourse(self) -> int:
712
+ return max(
713
+ 0,
714
+ self.max_unique_evaluations
715
+ - self.used_unique_evaluations
716
+ - self.reserved_non_recourse_evaluations
717
+ - self.protected_recombination_evaluations,
718
+ )
719
+
720
+ def to_trace_record(self) -> dict[str, int]:
721
+ return {
722
+ "max_unique_evaluations": self.max_unique_evaluations,
723
+ "used_unique_evaluations": self.used_unique_evaluations,
724
+ "reserved_non_recourse_evaluations": (
725
+ self.reserved_non_recourse_evaluations
726
+ ),
727
+ "protected_recombination_evaluations": (
728
+ self.protected_recombination_evaluations
729
+ ),
730
+ "available_for_recourse": self.available_for_recourse,
731
+ }
732
+
733
+
734
+ class RecourseDecisionReason(str, Enum):
735
+ SELECTED = "selected"
736
+ PRIMARY_BLOCK_INVALID = "primary_block_invalid"
737
+ MAX_RECOURSE_ZERO = "max_recourse_zero"
738
+ NO_SUCCESSFUL_PRIMARY_COLLISION = "no_successful_primary_collision"
739
+ NO_ELIGIBLE_PRESEALED_CANDIDATE = "no_eligible_presealed_candidate"
740
+ UNIQUE_BUDGET_PROTECTED = "unique_budget_protected"
741
+
742
+
743
+ def _eligible_pool_entries(
744
+ ledger: PhenotypeOccurrenceLedger,
745
+ pool: PresealedRecoursePool,
746
+ ) -> tuple[RecoursePoolEntry, ...]:
747
+ # Recourse outcomes are intentionally absent: they cannot change this pool
748
+ # or trigger a second recourse wave. Every primary identity-bearing outcome
749
+ # is excluded because repeating it cannot require a new physical evaluation.
750
+ occupied = ledger.primary_phenotypes
751
+ return tuple(entry for entry in pool.entries if entry.phenotype not in occupied)
752
+
753
+
754
+ def _decision_facts(
755
+ *,
756
+ ledger: PhenotypeOccurrenceLedger,
757
+ pool: PresealedRecoursePool,
758
+ budget: RecourseBudgetSnapshot,
759
+ max_recourse: int,
760
+ ) -> tuple[
761
+ tuple[RecoursePoolEntry, ...],
762
+ int,
763
+ RecourseDecisionReason,
764
+ ]:
765
+ eligible = _eligible_pool_entries(ledger, pool)
766
+ invalidating = ledger.invalidating_primary_trial_ids
767
+ credit = ledger.successful_primary_collision_credit
768
+ if invalidating:
769
+ return eligible, 0, RecourseDecisionReason.PRIMARY_BLOCK_INVALID
770
+
771
+ slots = min(
772
+ max_recourse,
773
+ credit,
774
+ budget.available_for_recourse,
775
+ len(eligible),
776
+ )
777
+ if slots > 0:
778
+ return eligible, slots, RecourseDecisionReason.SELECTED
779
+ if max_recourse == 0:
780
+ reason = RecourseDecisionReason.MAX_RECOURSE_ZERO
781
+ elif credit == 0:
782
+ reason = RecourseDecisionReason.NO_SUCCESSFUL_PRIMARY_COLLISION
783
+ elif not eligible:
784
+ reason = RecourseDecisionReason.NO_ELIGIBLE_PRESEALED_CANDIDATE
785
+ else:
786
+ reason = RecourseDecisionReason.UNIQUE_BUDGET_PROTECTED
787
+ return eligible, 0, reason
788
+
789
+
790
+ @dataclass(frozen=True, slots=True, eq=False)
791
+ class EvaluationRecourseDecision:
792
+ """Complete, replayable, objective-free recourse decision."""
793
+
794
+ ledger: PhenotypeOccurrenceLedger
795
+ pool: PresealedRecoursePool
796
+ budget: RecourseBudgetSnapshot
797
+ max_recourse: int
798
+ selected_entry_ids: tuple[str, ...]
799
+ reason: RecourseDecisionReason
800
+
801
+ policy_id: ClassVar[str] = RECOURSE_POLICY_ID
802
+ policy_version: ClassVar[int] = RECOURSE_POLICY_VERSION
803
+
804
+ def __post_init__(self) -> None:
805
+ if type(self.ledger) is not PhenotypeOccurrenceLedger:
806
+ raise TypeError("ledger must be an exact PhenotypeOccurrenceLedger")
807
+ PhenotypeOccurrenceLedger.__post_init__(self.ledger)
808
+ if type(self.pool) is not PresealedRecoursePool:
809
+ raise TypeError("pool must be an exact PresealedRecoursePool")
810
+ PresealedRecoursePool.__post_init__(self.pool)
811
+ if type(self.budget) is not RecourseBudgetSnapshot:
812
+ raise TypeError("budget must be an exact RecourseBudgetSnapshot")
813
+ RecourseBudgetSnapshot.__post_init__(self.budget)
814
+ _nonnegative(self.max_recourse, name="max_recourse")
815
+ if (
816
+ self.ledger.identity_policy_id != self.pool.identity_policy_id
817
+ or self.ledger.identity_policy_version != self.pool.identity_policy_version
818
+ ):
819
+ raise ValueError("ledger and pool identity policies differ")
820
+ if type(self.selected_entry_ids) is not tuple or any(
821
+ type(item) is not str for item in self.selected_entry_ids
822
+ ):
823
+ raise TypeError("selected_entry_ids must be an exact string tuple")
824
+ if type(self.reason) is not RecourseDecisionReason:
825
+ raise TypeError("reason must be a RecourseDecisionReason")
826
+ eligible, slots, expected_reason = _decision_facts(
827
+ ledger=self.ledger,
828
+ pool=self.pool,
829
+ budget=self.budget,
830
+ max_recourse=self.max_recourse,
831
+ )
832
+ expected_ids = tuple(entry.entry_id for entry in eligible[:slots])
833
+ if self.selected_entry_ids != expected_ids:
834
+ raise ValueError("selected entries do not match the bounded prefix rule")
835
+ if self.reason is not expected_reason:
836
+ raise ValueError("decision reason does not match the bounded rule")
837
+
838
+ @property
839
+ def eligible_entries(self) -> tuple[RecoursePoolEntry, ...]:
840
+ return _eligible_pool_entries(self.ledger, self.pool)
841
+
842
+ @property
843
+ def selected_entries(self) -> tuple[RecoursePoolEntry, ...]:
844
+ by_id = {entry.entry_id: entry for entry in self.pool.entries}
845
+ return tuple(by_id[entry_id] for entry_id in self.selected_entry_ids)
846
+
847
+ @property
848
+ def slots(self) -> int:
849
+ return len(self.selected_entry_ids)
850
+
851
+ def _trace_payload(self) -> dict[str, object]:
852
+ return {
853
+ "event_type": "evaluation_recourse_decided",
854
+ "policy_id": self.policy_id,
855
+ "policy_version": self.policy_version,
856
+ "ledger_sha256": self.ledger.ledger_sha256,
857
+ "pool_sha256": self.pool.pool_sha256,
858
+ "budget": self.budget.to_trace_record(),
859
+ "max_recourse": self.max_recourse,
860
+ "successful_primary_collision_credit": (
861
+ self.ledger.successful_primary_collision_credit
862
+ ),
863
+ "collision_credit_source": "successful_primary_occurrences_only",
864
+ "invalidating_primary_trial_ids": [
865
+ item.value for item in self.ledger.invalidating_primary_trial_ids
866
+ ],
867
+ "ignored_recourse_trial_ids": [
868
+ item.value for item in self.ledger.ignored_recourse_trial_ids
869
+ ],
870
+ "eligible_entry_ids": [entry.entry_id for entry in self.eligible_entries],
871
+ "selected_entry_ids": list(self.selected_entry_ids),
872
+ "slots": self.slots,
873
+ "reason": self.reason.value,
874
+ "slot_formula": (
875
+ "min(max_recourse, successful_primary_collision_credit, "
876
+ "available_unique_budget_after_reservations_and_recombination, "
877
+ "eligible_presealed_pool_size)"
878
+ ),
879
+ }
880
+
881
+ @property
882
+ def decision_sha256(self) -> str:
883
+ return _domain_hash(_DECISION_DOMAIN, self._trace_payload())
884
+
885
+ def to_trace_record(self) -> dict[str, object]:
886
+ return {**self._trace_payload(), "decision_sha256": self.decision_sha256}
887
+
888
+ def __eq__(self, other: object) -> bool:
889
+ return (
890
+ type(self) is EvaluationRecourseDecision
891
+ and type(other) is EvaluationRecourseDecision
892
+ and self.decision_sha256 == other.decision_sha256
893
+ )
894
+
895
+ __hash__ = None
896
+
897
+
898
+ class EvaluationRecoursePolicy(Protocol):
899
+ """Objective-blind policy over identities, statuses, and reservations only."""
900
+
901
+ def decide(
902
+ self,
903
+ *,
904
+ ledger: PhenotypeOccurrenceLedger,
905
+ pool: PresealedRecoursePool,
906
+ budget: RecourseBudgetSnapshot,
907
+ ) -> EvaluationRecourseDecision: ...
908
+
909
+
910
+ @dataclass(frozen=True, slots=True)
911
+ class BoundedEvaluationRecoursePolicy:
912
+ """Choose a deterministic prefix under collision and hard-budget caps."""
913
+
914
+ max_recourse: int
915
+
916
+ policy_id: ClassVar[str] = RECOURSE_POLICY_ID
917
+ policy_version: ClassVar[int] = RECOURSE_POLICY_VERSION
918
+
919
+ def __post_init__(self) -> None:
920
+ _nonnegative(self.max_recourse, name="max_recourse")
921
+
922
+ def decide(
923
+ self,
924
+ *,
925
+ ledger: PhenotypeOccurrenceLedger,
926
+ pool: PresealedRecoursePool,
927
+ budget: RecourseBudgetSnapshot,
928
+ ) -> EvaluationRecourseDecision:
929
+ for value, expected, name in (
930
+ (ledger, PhenotypeOccurrenceLedger, "ledger"),
931
+ (pool, PresealedRecoursePool, "pool"),
932
+ (budget, RecourseBudgetSnapshot, "budget"),
933
+ ):
934
+ if type(value) is not expected:
935
+ raise TypeError(f"{name} must be an exact {expected.__name__}")
936
+ if (
937
+ ledger.identity_policy_id != pool.identity_policy_id
938
+ or ledger.identity_policy_version != pool.identity_policy_version
939
+ ):
940
+ raise ValueError("ledger and pool identity policies differ")
941
+ eligible, slots, reason = _decision_facts(
942
+ ledger=ledger,
943
+ pool=pool,
944
+ budget=budget,
945
+ max_recourse=self.max_recourse,
946
+ )
947
+ return EvaluationRecourseDecision(
948
+ ledger=ledger,
949
+ pool=pool,
950
+ budget=budget,
951
+ max_recourse=self.max_recourse,
952
+ selected_entry_ids=tuple(entry.entry_id for entry in eligible[:slots]),
953
+ reason=reason,
954
+ )
955
+
956
+
957
+ __all__ = [
958
+ "BoundedEvaluationRecoursePolicy",
959
+ "EvaluationOccurrenceRole",
960
+ "EvaluationOccurrenceStatus",
961
+ "EvaluationRecourseDecision",
962
+ "EvaluationRecoursePolicy",
963
+ "IDENTITY_POLICY_ID",
964
+ "IDENTITY_POLICY_VERSION",
965
+ "PhenotypeCluster",
966
+ "PhenotypeIdentity",
967
+ "PhenotypeIdentityPolicy",
968
+ "PhenotypeOccurrence",
969
+ "PhenotypeOccurrenceLedger",
970
+ "PresealedRecoursePool",
971
+ "RECOURSE_POLICY_ID",
972
+ "RECOURSE_POLICY_VERSION",
973
+ "RecourseBudgetSnapshot",
974
+ "RecourseDecisionReason",
975
+ "RecoursePoolCandidate",
976
+ "RecoursePoolEntry",
977
+ "SemanticProjectionPhenotypeIdentityPolicy",
978
+ "TypedConfigurationPhenotypeIdentityPolicy",
979
+ ]