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,1828 @@
1
+ """Budgeted orchestration for explicit agentic evolution generations.
2
+
3
+ The :class:`AgenticEvolutionEngine` deliberately does not choose generations.
4
+ This module supplies the missing application boundary: a planner receives an
5
+ immutable history/archive cutoff, returns one ordered wave, and the optimizer
6
+ reserves hard budgets before either a model call or an evaluation can start.
7
+
8
+ The coordinator is domain- and provider-agnostic. Model-authored and
9
+ engine-materialized slots execute concurrently, but candidates are appended to
10
+ history and published to the Pareto archive in the planner's frozen slot order.
11
+ Every wave uses one reward binding tied to the exact pre-wave archive cutoff.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ import hashlib
18
+ import json
19
+ import math
20
+ import time
21
+ from collections.abc import Callable, Mapping, Sequence
22
+ from dataclasses import dataclass
23
+ from enum import Enum
24
+ from fractions import Fraction
25
+ from typing import Protocol
26
+
27
+ from agent_evolve.application.agentic_evolution import (
28
+ AgenticEvolutionEngine,
29
+ EvolutionCandidate,
30
+ InvocationOutcome,
31
+ InvocationPlan,
32
+ MaterializedInvocation,
33
+ OperatorKind,
34
+ ProposalAuthority,
35
+ RewardPolicyBinding,
36
+ )
37
+ from agent_evolve.application.generation_feedback import (
38
+ GenerationFeedbackContext,
39
+ GenerationFeedbackInterceptor,
40
+ GenerationFeedbackReceipt,
41
+ GenerationFeedbackReservation,
42
+ GenerationFeedbackResult,
43
+ seal_generation_feedback,
44
+ validate_generation_feedback_receipt,
45
+ )
46
+ from agent_evolve.application.pareto_archive import (
47
+ ParetoArchive,
48
+ ParetoArchiveSnapshot,
49
+ ParetoDecision,
50
+ pareto_candidate_hash,
51
+ )
52
+ from agent_evolve.domain.patch import canonical_path_bytes, require_sha256
53
+ from agent_evolve.domain.typed_json import freeze_json, typed_json_sha256
54
+ from agent_evolve.policies.variation.exact_parent_crossover import (
55
+ exact_parent_import_exclusions_sha256,
56
+ )
57
+ from agent_evolve.ports.agentic_generator import AtomicMutationDraft, CandidateDraft
58
+
59
+
60
+ OptimizerTraceSink = Callable[[Mapping[str, object]], None]
61
+ _HASH_DOMAIN = b"agent-evolve:budgeted-agentic-optimizer:v1\x00"
62
+
63
+
64
+ class OptimizerContractError(ValueError):
65
+ """A planner, budget, or optimizer input violated the frozen contract."""
66
+
67
+
68
+ class OptimizerBudgetExceeded(OptimizerContractError):
69
+ """A complete wave could exceed a hard resource cap."""
70
+
71
+
72
+ class OptimizerPlanningError(RuntimeError):
73
+ """The injected generation planner failed before a wave was admitted."""
74
+
75
+
76
+ class OptimizerExecutionError(RuntimeError):
77
+ """An admitted wave failed outside the engine's typed outcome boundary."""
78
+
79
+
80
+ class OptimizerStopReason(str, Enum):
81
+ GENERATION_LIMIT_REACHED = "generation_limit_reached"
82
+
83
+
84
+ def _canonical_json(value: object) -> bytes:
85
+ return json.dumps(
86
+ value,
87
+ ensure_ascii=True,
88
+ allow_nan=False,
89
+ separators=(",", ":"),
90
+ sort_keys=True,
91
+ ).encode("ascii")
92
+
93
+
94
+ def _record_hash(kind: str, value: object) -> str:
95
+ if type(kind) is not str or not kind:
96
+ raise ValueError("hash record kind must be non-empty")
97
+ return hashlib.sha256(
98
+ _HASH_DOMAIN + kind.encode("ascii") + b"\x00" + _canonical_json(value)
99
+ ).hexdigest()
100
+
101
+
102
+ def pareto_archive_snapshot_hash(snapshot: ParetoArchiveSnapshot) -> str:
103
+ """Hash the complete immutable archive cutoff, including its decision ledger."""
104
+
105
+ if type(snapshot) is not ParetoArchiveSnapshot:
106
+ raise TypeError("snapshot must be an exact ParetoArchiveSnapshot")
107
+ record = {
108
+ "objectives": [
109
+ {"name": objective.name, "goal": objective.goal}
110
+ for objective in snapshot.objectives
111
+ ],
112
+ "front": [
113
+ reference.to_trace_record() for reference in snapshot.front_references
114
+ ],
115
+ "decisions": [decision.to_trace_record() for decision in snapshot.decisions],
116
+ "consideration_count": snapshot.consideration_count,
117
+ "eligible_configuration_count": snapshot.eligible_configuration_count,
118
+ "evidence_admission_policy": snapshot.evidence_admission_policy.value,
119
+ }
120
+ if not snapshot.objective_pareto_relation:
121
+ record["outcome_relation_policy"] = {
122
+ "policy_id": snapshot.outcome_relation_policy[0],
123
+ "policy_version": snapshot.outcome_relation_policy[1],
124
+ "definition_sha256": snapshot.outcome_relation_policy[2],
125
+ }
126
+ return _record_hash("pareto-archive-snapshot", record)
127
+
128
+
129
+ @dataclass(frozen=True, slots=True)
130
+ class OptimizerBudget:
131
+ """Hard run-level caps; retries remain owned by the provider queue."""
132
+
133
+ max_unique_evaluations: int
134
+ max_logical_llm_calls: int
135
+ max_generations: int
136
+
137
+ def __post_init__(self) -> None:
138
+ if (
139
+ type(self.max_unique_evaluations) is not int
140
+ or self.max_unique_evaluations <= 0
141
+ ):
142
+ raise ValueError("max_unique_evaluations must be a positive integer")
143
+ if (
144
+ type(self.max_logical_llm_calls) is not int
145
+ or self.max_logical_llm_calls < 0
146
+ ):
147
+ raise ValueError("max_logical_llm_calls must be a non-negative integer")
148
+ if type(self.max_generations) is not int or self.max_generations < 0:
149
+ raise ValueError("max_generations must be a non-negative integer")
150
+
151
+ def to_trace_record(self) -> dict[str, int]:
152
+ return {
153
+ "max_unique_evaluations": self.max_unique_evaluations,
154
+ "max_logical_llm_calls": self.max_logical_llm_calls,
155
+ "max_generations": self.max_generations,
156
+ }
157
+
158
+ @property
159
+ def budget_hash(self) -> str:
160
+ return _record_hash("budget", self.to_trace_record())
161
+
162
+
163
+ @dataclass(frozen=True, slots=True)
164
+ class FrozenWaveReward:
165
+ """One reward policy frozen against one explicit pre-wave evidence cutoff."""
166
+
167
+ binding: RewardPolicyBinding
168
+ archive_snapshot_hash: str
169
+ reward_snapshot_hash: str
170
+
171
+ def __post_init__(self) -> None:
172
+ if type(self.binding) is not RewardPolicyBinding:
173
+ raise TypeError("binding must be an exact RewardPolicyBinding")
174
+ RewardPolicyBinding.__post_init__(self.binding)
175
+ require_sha256(self.archive_snapshot_hash, "archive_snapshot_hash")
176
+ require_sha256(self.reward_snapshot_hash, "reward_snapshot_hash")
177
+
178
+
179
+ @dataclass(frozen=True, slots=True)
180
+ class SeedGateContext:
181
+ """Run facts available to a domain-specific seed admission policy."""
182
+
183
+ seed_index: int
184
+ label: str
185
+ requested_configuration_hash: str
186
+ unique_evaluations_before: int
187
+ unique_evaluations_after: int
188
+
189
+ def __post_init__(self) -> None:
190
+ if type(self.seed_index) is not int or self.seed_index < 0:
191
+ raise ValueError("seed_index must be non-negative")
192
+ if type(self.label) is not str or not self.label:
193
+ raise ValueError("label must be non-empty")
194
+ require_sha256(
195
+ self.requested_configuration_hash,
196
+ "requested_configuration_hash",
197
+ )
198
+ for name in ("unique_evaluations_before", "unique_evaluations_after"):
199
+ value = getattr(self, name)
200
+ if type(value) is not int or value < 0:
201
+ raise ValueError(f"{name} must be non-negative")
202
+ if self.unique_evaluations_after < self.unique_evaluations_before:
203
+ raise ValueError("seed evaluation counters cannot decrease")
204
+
205
+
206
+ @dataclass(frozen=True, slots=True)
207
+ class SeedGateDecision:
208
+ """Versioned seed identity/objective/provenance admission evidence."""
209
+
210
+ admitted: bool
211
+ policy_id: str
212
+ policy_version: int
213
+ reason: str
214
+ evidence: tuple[tuple[str, str], ...] = ()
215
+
216
+ def __post_init__(self) -> None:
217
+ if type(self.admitted) is not bool:
218
+ raise TypeError("admitted must be bool")
219
+ if (
220
+ type(self.policy_id) is not str
221
+ or not self.policy_id
222
+ or self.policy_id != self.policy_id.strip()
223
+ ):
224
+ raise ValueError("policy_id must be canonical non-empty text")
225
+ if type(self.policy_version) is not int or self.policy_version <= 0:
226
+ raise ValueError("policy_version must be positive")
227
+ if type(self.reason) is not str or not self.reason.strip():
228
+ raise ValueError("reason must be non-empty")
229
+ if type(self.evidence) is not tuple:
230
+ raise TypeError("evidence must be an exact tuple")
231
+ for item in self.evidence:
232
+ if (
233
+ type(item) is not tuple
234
+ or len(item) != 2
235
+ or any(type(value) is not str for value in item)
236
+ ):
237
+ raise TypeError("evidence must contain exact string pairs")
238
+ if self.evidence != tuple(sorted(set(self.evidence))):
239
+ raise ValueError("evidence must be unique and canonically sorted")
240
+
241
+ def to_trace_record(self) -> dict[str, object]:
242
+ record = {
243
+ "admitted": self.admitted,
244
+ "policy_id": self.policy_id,
245
+ "policy_version": self.policy_version,
246
+ "reason": self.reason,
247
+ "evidence": [list(item) for item in self.evidence],
248
+ }
249
+ return {
250
+ **record,
251
+ "decision_hash": _record_hash("seed-gate-decision", record),
252
+ }
253
+
254
+
255
+ class SeedAdmissionPolicy(Protocol):
256
+ """Domain gate for exact seed identity, objectives, and external provenance."""
257
+
258
+ def assess(
259
+ self,
260
+ candidate: EvolutionCandidate,
261
+ context: SeedGateContext,
262
+ ) -> SeedGateDecision: ...
263
+
264
+
265
+ class ValidSeedAdmissionPolicy:
266
+ """Default gate: require a valid evaluated seed with a complete objective vector."""
267
+
268
+ policy_id = "valid_evaluated_seed"
269
+ policy_version = 1
270
+
271
+ def assess(
272
+ self,
273
+ candidate: EvolutionCandidate,
274
+ context: SeedGateContext,
275
+ ) -> SeedGateDecision:
276
+ del context
277
+ complete = candidate.valid and len(candidate.objectives) > 0
278
+ return SeedGateDecision(
279
+ admitted=complete,
280
+ policy_id=self.policy_id,
281
+ policy_version=self.policy_version,
282
+ reason=(
283
+ "seed is valid with a non-empty objective vector"
284
+ if complete
285
+ else "seed is invalid or lacks an objective vector"
286
+ ),
287
+ evidence=(
288
+ ("candidate_hash", pareto_candidate_hash(candidate)),
289
+ ("valid", str(candidate.valid).lower()),
290
+ ),
291
+ )
292
+
293
+
294
+ @dataclass(frozen=True, slots=True)
295
+ class OptimizerSlot:
296
+ """One ordered generation slot with explicit proposal authority."""
297
+
298
+ slot_id: str
299
+ role: str
300
+ proposal_authority: ProposalAuthority
301
+ plan: InvocationPlan
302
+ materialized: MaterializedInvocation | None = None
303
+
304
+ def __post_init__(self) -> None:
305
+ for name in ("slot_id", "role"):
306
+ value = getattr(self, name)
307
+ if type(value) is not str or not value or value != value.strip():
308
+ raise ValueError(f"{name} must be canonical non-empty text")
309
+ if type(self.proposal_authority) is not ProposalAuthority:
310
+ raise TypeError("proposal_authority must be a ProposalAuthority")
311
+ if type(self.plan) is not InvocationPlan:
312
+ raise TypeError("plan must be an exact InvocationPlan")
313
+ InvocationPlan.__post_init__(self.plan)
314
+ if self.proposal_authority is ProposalAuthority.ENGINE:
315
+ if type(self.materialized) is not MaterializedInvocation:
316
+ raise ValueError("engine authority requires a materialized invocation")
317
+ MaterializedInvocation.__post_init__(self.materialized)
318
+ if self.materialized.plan != self.plan:
319
+ raise ValueError("slot plan and materialized invocation plan differ")
320
+ elif self.materialized is not None:
321
+ raise ValueError("only engine authority accepts a materialized invocation")
322
+ if self.proposal_authority is ProposalAuthority.MODEL:
323
+ if self.plan.operator_kind is OperatorKind.REPRODUCTION:
324
+ raise ValueError("reproduction is not a model-authored proposal")
325
+ elif self.proposal_authority is ProposalAuthority.REPRODUCTION:
326
+ if self.plan.operator_kind is not OperatorKind.REPRODUCTION:
327
+ raise ValueError("reproduction authority requires a reproduction plan")
328
+ elif self.plan.operator_kind is OperatorKind.REPRODUCTION:
329
+ raise ValueError("reproduction plans require reproduction authority")
330
+
331
+ @classmethod
332
+ def model(
333
+ cls,
334
+ *,
335
+ slot_id: str,
336
+ role: str,
337
+ plan: InvocationPlan,
338
+ ) -> "OptimizerSlot":
339
+ return cls(slot_id, role, ProposalAuthority.MODEL, plan)
340
+
341
+ @classmethod
342
+ def engine(
343
+ cls,
344
+ *,
345
+ slot_id: str,
346
+ role: str,
347
+ invocation: MaterializedInvocation,
348
+ ) -> "OptimizerSlot":
349
+ if type(invocation) is not MaterializedInvocation:
350
+ raise TypeError("invocation must be an exact MaterializedInvocation")
351
+ return cls(
352
+ slot_id,
353
+ role,
354
+ ProposalAuthority.ENGINE,
355
+ invocation.plan,
356
+ invocation,
357
+ )
358
+
359
+ @classmethod
360
+ def reproduction(
361
+ cls,
362
+ *,
363
+ slot_id: str,
364
+ role: str,
365
+ plan: InvocationPlan,
366
+ ) -> "OptimizerSlot":
367
+ return cls(slot_id, role, ProposalAuthority.REPRODUCTION, plan)
368
+
369
+ @property
370
+ def logical_llm_call_reservation(self) -> int:
371
+ return int(self.proposal_authority is ProposalAuthority.MODEL)
372
+
373
+ @property
374
+ def unique_evaluation_reservation(self) -> int:
375
+ # Reproduction is guaranteed to reuse the exact parent configuration.
376
+ # Every other slot may require one new physical evaluation; reserving the
377
+ # upper bound is what makes concurrent admission safe.
378
+ return int(self.proposal_authority is not ProposalAuthority.REPRODUCTION)
379
+
380
+
381
+ @dataclass(frozen=True, slots=True)
382
+ class GenerationPlan:
383
+ """An immutable, ordered wave returned by a generation policy."""
384
+
385
+ generation: int
386
+ slots: tuple[OptimizerSlot, ...]
387
+ reward: FrozenWaveReward
388
+ planner_policy_id: str
389
+ planner_policy_version: int
390
+ metadata: tuple[tuple[str, str], ...] = ()
391
+
392
+ def __post_init__(self) -> None:
393
+ if type(self.generation) is not int or self.generation <= 0:
394
+ raise ValueError("generation must be a positive integer")
395
+ if type(self.slots) is not tuple or any(
396
+ type(slot) is not OptimizerSlot for slot in self.slots
397
+ ):
398
+ raise TypeError("slots must contain exact OptimizerSlot values")
399
+ if len({slot.slot_id for slot in self.slots}) != len(self.slots):
400
+ raise ValueError("generation slot IDs must be unique")
401
+ if any(slot.plan.generation != self.generation for slot in self.slots):
402
+ raise ValueError("every invocation must target the plan generation")
403
+ if type(self.reward) is not FrozenWaveReward:
404
+ raise TypeError("reward must be an exact FrozenWaveReward")
405
+ FrozenWaveReward.__post_init__(self.reward)
406
+ if (
407
+ type(self.planner_policy_id) is not str
408
+ or not self.planner_policy_id
409
+ or self.planner_policy_id != self.planner_policy_id.strip()
410
+ ):
411
+ raise ValueError("planner_policy_id must be canonical non-empty text")
412
+ if (
413
+ type(self.planner_policy_version) is not int
414
+ or self.planner_policy_version <= 0
415
+ ):
416
+ raise ValueError("planner_policy_version must be positive")
417
+ if type(self.metadata) is not tuple:
418
+ raise TypeError("metadata must be an exact tuple")
419
+ for item in self.metadata:
420
+ if (
421
+ type(item) is not tuple
422
+ or len(item) != 2
423
+ or any(type(value) is not str for value in item)
424
+ ):
425
+ raise TypeError("metadata must contain exact string pairs")
426
+ if self.metadata != tuple(sorted(set(self.metadata))):
427
+ raise ValueError("metadata must be unique and canonically sorted")
428
+
429
+ @property
430
+ def logical_llm_call_reservation(self) -> int:
431
+ return sum(slot.logical_llm_call_reservation for slot in self.slots)
432
+
433
+ @property
434
+ def unique_evaluation_reservation(self) -> int:
435
+ return sum(slot.unique_evaluation_reservation for slot in self.slots)
436
+
437
+
438
+ @dataclass(frozen=True, slots=True)
439
+ class OptimizerState:
440
+ """Immutable planner view after an entire generation has been published."""
441
+
442
+ generation: int
443
+ candidates: tuple[EvolutionCandidate, ...]
444
+ archive: ParetoArchiveSnapshot
445
+ archive_snapshot_hash: str
446
+ unique_evaluations: int
447
+ logical_llm_calls: int
448
+ generation_receipts: tuple["GenerationReceipt", ...] = ()
449
+ feedback_receipts: tuple[GenerationFeedbackReceipt, ...] = ()
450
+
451
+ def __post_init__(self) -> None:
452
+ if type(self.generation) is not int or self.generation < 0:
453
+ raise ValueError("generation must be non-negative")
454
+ if type(self.candidates) is not tuple or any(
455
+ type(candidate) is not EvolutionCandidate for candidate in self.candidates
456
+ ):
457
+ raise TypeError("candidates must contain exact EvolutionCandidate values")
458
+ if len({candidate.candidate_id for candidate in self.candidates}) != len(
459
+ self.candidates
460
+ ):
461
+ raise ValueError("candidate history contains duplicate occurrence IDs")
462
+ if type(self.archive) is not ParetoArchiveSnapshot:
463
+ raise TypeError("archive must be an exact ParetoArchiveSnapshot")
464
+ require_sha256(self.archive_snapshot_hash, "archive_snapshot_hash")
465
+ if self.archive_snapshot_hash != pareto_archive_snapshot_hash(self.archive):
466
+ raise ValueError("archive_snapshot_hash does not identify archive")
467
+ for name in ("unique_evaluations", "logical_llm_calls"):
468
+ value = getattr(self, name)
469
+ if type(value) is not int or value < 0:
470
+ raise ValueError(f"{name} must be a non-negative integer")
471
+ if type(self.generation_receipts) is not tuple or any(
472
+ type(receipt) is not GenerationReceipt
473
+ for receipt in self.generation_receipts
474
+ ):
475
+ raise TypeError(
476
+ "generation_receipts must contain exact GenerationReceipt values"
477
+ )
478
+ for receipt in self.generation_receipts:
479
+ validate_generation_receipt_integrity(receipt)
480
+ receipt_generations = tuple(
481
+ receipt.generation for receipt in self.generation_receipts
482
+ )
483
+ if receipt_generations != tuple(range(1, len(receipt_generations) + 1)):
484
+ raise ValueError("generation_receipts must be contiguous and ordered")
485
+ if self.generation_receipts and receipt_generations[-1] > self.generation:
486
+ raise ValueError("generation_receipts cannot be newer than planner state")
487
+ if type(self.feedback_receipts) is not tuple or any(
488
+ type(receipt) is not GenerationFeedbackReceipt
489
+ for receipt in self.feedback_receipts
490
+ ):
491
+ raise TypeError(
492
+ "feedback_receipts must contain exact GenerationFeedbackReceipt values"
493
+ )
494
+ for receipt in self.feedback_receipts:
495
+ validate_generation_feedback_receipt(receipt)
496
+ feedback_generations = tuple(
497
+ receipt.generation for receipt in self.feedback_receipts
498
+ )
499
+ if feedback_generations != tuple(range(1, len(feedback_generations) + 1)):
500
+ raise ValueError("feedback_receipts must be contiguous and ordered")
501
+ if self.feedback_receipts and feedback_generations[-1] > self.generation:
502
+ raise ValueError("feedback_receipts cannot be newer than planner state")
503
+ if len(self.feedback_receipts) > len(self.generation_receipts):
504
+ raise ValueError("feedback receipts cannot outnumber generation receipts")
505
+ for feedback_receipt in self.feedback_receipts:
506
+ generation_receipt = self.generation_receipts[
507
+ feedback_receipt.generation - 1
508
+ ]
509
+ if (
510
+ feedback_receipt.generation_receipt_hash
511
+ != generation_receipt.receipt_hash
512
+ ):
513
+ raise ValueError(
514
+ "feedback receipt does not identify its generation receipt"
515
+ )
516
+ if self.feedback_receipts and (
517
+ self.feedback_receipts[-1].logical_llm_calls_after > self.logical_llm_calls
518
+ ):
519
+ raise ValueError("feedback receipt exceeds planner logical-call state")
520
+
521
+
522
+ class GenerationPlanner(Protocol):
523
+ """Injected, deterministic generation policy."""
524
+
525
+ def plan(
526
+ self,
527
+ state: OptimizerState,
528
+ budget: OptimizerBudget,
529
+ ) -> GenerationPlan: ...
530
+
531
+
532
+ @dataclass(frozen=True, slots=True)
533
+ class SeedReceipt:
534
+ label: str
535
+ candidate: EvolutionCandidate
536
+ gate_decision: SeedGateDecision
537
+ archive_decisions: tuple[ParetoDecision, ...]
538
+ unique_evaluations_before: int
539
+ unique_evaluations_after: int
540
+ archive_snapshot_hash: str
541
+ receipt_hash: str
542
+
543
+
544
+ @dataclass(frozen=True, slots=True)
545
+ class SlotResult:
546
+ slot: OptimizerSlot
547
+ outcome: InvocationOutcome
548
+ archive_decisions: tuple[ParetoDecision, ...]
549
+
550
+
551
+ @dataclass(frozen=True, slots=True)
552
+ class GenerationReceipt:
553
+ generation: int
554
+ plan_hash: str
555
+ pre_archive_snapshot_hash: str
556
+ post_archive_snapshot_hash: str
557
+ reward_definition_hash: str
558
+ reward_snapshot_hash: str
559
+ logical_llm_calls_before: int
560
+ logical_llm_calls_after: int
561
+ unique_evaluations_before: int
562
+ unique_evaluations_after: int
563
+ reserved_logical_llm_calls: int
564
+ reserved_unique_evaluations: int
565
+ slot_results: tuple[SlotResult, ...]
566
+ receipt_hash: str
567
+
568
+
569
+ @dataclass(frozen=True, slots=True)
570
+ class OptimizerResult:
571
+ budget: OptimizerBudget
572
+ final_state: OptimizerState
573
+ seed_receipts: tuple[SeedReceipt, ...]
574
+ generation_receipts: tuple[GenerationReceipt, ...]
575
+ stop_reason: OptimizerStopReason
576
+ result_hash: str
577
+ feedback_receipts: tuple[GenerationFeedbackReceipt, ...] = ()
578
+
579
+
580
+ def _fraction_record(value: Fraction | None) -> object:
581
+ if value is None:
582
+ return None
583
+ return {"numerator": value.numerator, "denominator": value.denominator}
584
+
585
+
586
+ def _candidate_identity(candidate: EvolutionCandidate | None) -> object:
587
+ if candidate is None:
588
+ return None
589
+ return {
590
+ "candidate_id": candidate.candidate_id.value,
591
+ "candidate_hash": pareto_candidate_hash(candidate),
592
+ "configuration_hash": candidate.occurrence.configuration_hash,
593
+ }
594
+
595
+
596
+ def _invocation_plan_record(plan: InvocationPlan) -> dict[str, object]:
597
+ contract = plan.mutation_contract
598
+ record: dict[str, object] = {
599
+ "operator_kind": plan.operator_kind.value,
600
+ "generation": plan.generation,
601
+ "label": plan.label,
602
+ "parents": [_candidate_identity(parent) for parent in plan.parents],
603
+ "common_ancestor": _candidate_identity(plan.common_ancestor),
604
+ "allowed_top_level": list(plan.allowed_top_level),
605
+ "phase": plan.phase,
606
+ "use_memory": plan.use_memory,
607
+ "memory_subset_size": plan.memory_subset_size,
608
+ "memory_exploration_probability": _fraction_record(
609
+ plan.memory_exploration_probability
610
+ ),
611
+ "memory_score_phase": plan.memory_score_phase,
612
+ "mutation_response_mode": plan.mutation_response_mode.value,
613
+ "mutation_contract": (
614
+ None
615
+ if contract is None
616
+ else {
617
+ "editable_path_hashes": [
618
+ hashlib.sha256(canonical_path_bytes(path)).hexdigest()
619
+ for path in contract.editable_paths
620
+ ],
621
+ "max_changed_paths": contract.max_changed_paths,
622
+ "max_operations": contract.max_operations,
623
+ "allow_abstention": contract.allow_abstention,
624
+ }
625
+ ),
626
+ "atomic_replacement_option_hashes": [
627
+ typed_json_sha256(option) for option in plan.atomic_replacement_options
628
+ ],
629
+ "quarantine_test_insights": [
630
+ {"insight_id": ref.insight_id.value, "version": ref.version}
631
+ for ref in plan.quarantine_test_insights
632
+ ],
633
+ "resolved_insight_assignment": (
634
+ None
635
+ if plan.resolved_insight_assignment is None
636
+ else {
637
+ **plan.resolved_insight_assignment.to_record(),
638
+ "assignment_sha256": (
639
+ plan.resolved_insight_assignment.assignment_sha256
640
+ ),
641
+ }
642
+ ),
643
+ "insight_treatment_requirement": (
644
+ None
645
+ if plan.insight_treatment_requirement is None
646
+ else {
647
+ **plan.insight_treatment_requirement.to_record(),
648
+ "requirement_sha256": (
649
+ plan.insight_treatment_requirement.requirement_sha256
650
+ ),
651
+ }
652
+ ),
653
+ "compiled_hypothesis_treatment": (
654
+ None
655
+ if plan.compiled_hypothesis_treatment is None
656
+ else {
657
+ **plan.compiled_hypothesis_treatment.to_record(),
658
+ "binding_sha256": (plan.compiled_hypothesis_treatment.binding_sha256),
659
+ }
660
+ ),
661
+ "compiled_hypothesis_eligibility": [
662
+ {
663
+ **value.to_record(),
664
+ "binding_sha256": value.binding_sha256,
665
+ }
666
+ for value in plan.compiled_hypothesis_eligibility
667
+ ],
668
+ }
669
+ # The contract identity binds the exact parent, ordered palette, prompt
670
+ # semantics, and every sealed child. Omitting the key for legacy modes keeps
671
+ # their historical plan and generation receipt hashes byte-compatible.
672
+ if plan.finite_variation_contract is not None:
673
+ record["finite_variation_contract"] = (
674
+ plan.finite_variation_contract.evidence_record()
675
+ )
676
+ if plan.finite_action_set_authority is not None:
677
+ record["finite_action_set_authority"] = {
678
+ **plan.finite_action_set_authority.to_record(),
679
+ "authority_sha256": plan.finite_action_set_authority.authority_sha256,
680
+ }
681
+ # Keep the historical record shape byte-stable for the default/full
682
+ # crossover representation. Exact parent import is a distinct model
683
+ # action space, so its mode, complete machine contract, and independently
684
+ # recomputable contract identity must all be authenticated by the
685
+ # generation-plan hash.
686
+ if plan.exact_parent_crossover_contract is not None:
687
+ exact_contract = plan.exact_parent_crossover_contract
688
+ record["crossover_response_mode"] = plan.crossover_response_mode.value
689
+ record["exact_parent_crossover_contract"] = exact_contract.to_record()
690
+ record["exact_parent_crossover_contract_sha256"] = (
691
+ exact_contract.contract_sha256
692
+ )
693
+ record["forbidden_exact_parent_import_sets"] = [
694
+ list(value) for value in plan.forbidden_exact_parent_import_sets
695
+ ]
696
+ record["exact_parent_import_exclusions_sha256"] = (
697
+ exact_parent_import_exclusions_sha256(
698
+ exact_contract,
699
+ plan.forbidden_exact_parent_import_sets,
700
+ )
701
+ )
702
+ return record
703
+
704
+
705
+ def _materialized_draft_record(
706
+ draft: CandidateDraft | AtomicMutationDraft,
707
+ ) -> dict[str, object]:
708
+ if type(draft) is CandidateDraft:
709
+ return {
710
+ "kind": "candidate_draft",
711
+ "configuration_hash": typed_json_sha256(freeze_json(draft.configuration)),
712
+ "design_rationale_sha256": hashlib.sha256(
713
+ draft.design_rationale.encode("utf-8")
714
+ ).hexdigest(),
715
+ "intended_changes": list(draft.intended_changes),
716
+ "source_attribution": [
717
+ {"path": item.path, "source": item.source}
718
+ for item in draft.source_attribution
719
+ ],
720
+ "claimed_insight_ids": list(draft.claimed_insight_ids),
721
+ "claimed_preservation_obligation_ids": list(
722
+ draft.claimed_preservation_obligation_ids
723
+ ),
724
+ "conflict_resolutions": [
725
+ {
726
+ "relation_id": item.relation_id,
727
+ "choice": item.choice,
728
+ "explanation_sha256": hashlib.sha256(
729
+ item.explanation.encode("utf-8")
730
+ ).hexdigest(),
731
+ }
732
+ for item in draft.conflict_resolutions
733
+ ],
734
+ }
735
+ if type(draft) is AtomicMutationDraft:
736
+ return {
737
+ "kind": "atomic_mutation_draft",
738
+ "path_hash": hashlib.sha256(canonical_path_bytes(draft.path)).hexdigest(),
739
+ "replacement_hash": typed_json_sha256(draft.replacement),
740
+ "design_rationale_sha256": hashlib.sha256(
741
+ draft.design_rationale.encode("utf-8")
742
+ ).hexdigest(),
743
+ "claimed_insight_ids": list(draft.claimed_insight_ids),
744
+ }
745
+ raise TypeError("unsupported materialized draft")
746
+
747
+
748
+ def _slot_record(slot: OptimizerSlot) -> dict[str, object]:
749
+ materialized = slot.materialized
750
+ return {
751
+ "slot_id": slot.slot_id,
752
+ "role": slot.role,
753
+ "proposal_authority": slot.proposal_authority.value,
754
+ "logical_llm_call_reservation": slot.logical_llm_call_reservation,
755
+ "unique_evaluation_reservation": slot.unique_evaluation_reservation,
756
+ "invocation": _invocation_plan_record(slot.plan),
757
+ "materialized": (
758
+ None
759
+ if materialized is None
760
+ else {
761
+ "candidate_id": materialized.candidate_id.value,
762
+ "policy_id": materialized.materialization_policy_id,
763
+ "policy_version": materialized.materialization_policy_version,
764
+ "receipt_hash": materialized.materialization_receipt_hash,
765
+ "draft": _materialized_draft_record(materialized.draft),
766
+ }
767
+ ),
768
+ }
769
+
770
+
771
+ def _generation_plan_record(
772
+ plan: GenerationPlan,
773
+ *,
774
+ budget_hash: str,
775
+ ) -> dict[str, object]:
776
+ return {
777
+ "generation": plan.generation,
778
+ "planner_policy_id": plan.planner_policy_id,
779
+ "planner_policy_version": plan.planner_policy_version,
780
+ "metadata": [list(item) for item in plan.metadata],
781
+ "budget_hash": budget_hash,
782
+ "pre_archive_snapshot_hash": plan.reward.archive_snapshot_hash,
783
+ "reward_definition_hash": plan.reward.binding.definition_hash,
784
+ "reward_failure_score_hex": plan.reward.binding.failure_score.hex(),
785
+ "reward_binding_sha256": plan.reward.binding.binding_sha256,
786
+ "reward_snapshot_hash": plan.reward.reward_snapshot_hash,
787
+ "logical_llm_call_reservation": plan.logical_llm_call_reservation,
788
+ "unique_evaluation_reservation": plan.unique_evaluation_reservation,
789
+ "slots": [_slot_record(slot) for slot in plan.slots],
790
+ }
791
+
792
+
793
+ def _generation_receipt_record(
794
+ *,
795
+ generation: int,
796
+ plan_hash: str,
797
+ pre_archive_snapshot_hash: str,
798
+ post_archive_snapshot_hash: str,
799
+ reward_definition_hash: str,
800
+ reward_snapshot_hash: str,
801
+ logical_llm_calls_before: int,
802
+ logical_llm_calls_after: int,
803
+ unique_evaluations_before: int,
804
+ unique_evaluations_after: int,
805
+ reserved_logical_llm_calls: int,
806
+ reserved_unique_evaluations: int,
807
+ slot_results: tuple[SlotResult, ...],
808
+ ) -> dict[str, object]:
809
+ """Project the exact immutable fields authenticated by a receipt hash."""
810
+
811
+ if type(generation) is not int or generation <= 0:
812
+ raise ValueError("receipt generation must be a positive exact integer")
813
+ for name in (
814
+ "plan_hash",
815
+ "pre_archive_snapshot_hash",
816
+ "post_archive_snapshot_hash",
817
+ "reward_definition_hash",
818
+ "reward_snapshot_hash",
819
+ ):
820
+ require_sha256(locals()[name], name)
821
+ for name in (
822
+ "logical_llm_calls_before",
823
+ "logical_llm_calls_after",
824
+ "unique_evaluations_before",
825
+ "unique_evaluations_after",
826
+ "reserved_logical_llm_calls",
827
+ "reserved_unique_evaluations",
828
+ ):
829
+ value = locals()[name]
830
+ if type(value) is not int or value < 0:
831
+ raise ValueError(f"{name} must be a non-negative exact integer")
832
+ if logical_llm_calls_after < logical_llm_calls_before:
833
+ raise ValueError("logical LLM-call counters cannot decrease")
834
+ if unique_evaluations_after < unique_evaluations_before:
835
+ raise ValueError("unique-evaluation counters cannot decrease")
836
+ if type(slot_results) is not tuple:
837
+ raise TypeError("slot_results must be an exact tuple")
838
+ if any(type(result) is not SlotResult for result in slot_results):
839
+ raise TypeError("slot_results must contain exact SlotResult values")
840
+ if any(type(result.slot) is not OptimizerSlot for result in slot_results):
841
+ raise TypeError("receipt results must contain exact OptimizerSlot values")
842
+ if any(type(result.outcome) is not InvocationOutcome for result in slot_results):
843
+ raise TypeError("receipt results must contain exact InvocationOutcome values")
844
+ if any(
845
+ type(result.archive_decisions) is not tuple
846
+ or any(
847
+ type(decision) is not ParetoDecision
848
+ for decision in result.archive_decisions
849
+ )
850
+ for result in slot_results
851
+ ):
852
+ raise TypeError(
853
+ "receipt archive_decisions must contain exact ParetoDecision values"
854
+ )
855
+ if len({result.slot.slot_id for result in slot_results}) != len(slot_results):
856
+ raise OptimizerContractError("generation receipt repeats a slot ID")
857
+ for result in slot_results:
858
+ prepared = result.outcome.prepared
859
+ InvocationOutcome.__post_init__(result.outcome)
860
+ if result.slot.plan != prepared.plan:
861
+ raise OptimizerContractError(
862
+ "generation receipt slot plan differs from its prepared outcome"
863
+ )
864
+ if result.slot.proposal_authority is not prepared.proposal_authority:
865
+ raise OptimizerContractError(
866
+ "generation receipt slot authority differs from its outcome"
867
+ )
868
+ if result.slot.plan.generation != generation:
869
+ raise OptimizerContractError(
870
+ "generation receipt slot targets a different generation"
871
+ )
872
+ if prepared.variation_case.reward_definition_hash != reward_definition_hash:
873
+ raise OptimizerContractError(
874
+ "generation receipt outcome has a different reward definition"
875
+ )
876
+
877
+ expected_logical_reservation = sum(
878
+ result.slot.logical_llm_call_reservation for result in slot_results
879
+ )
880
+ expected_unique_reservation = sum(
881
+ result.slot.unique_evaluation_reservation for result in slot_results
882
+ )
883
+ if reserved_logical_llm_calls != expected_logical_reservation:
884
+ raise OptimizerContractError(
885
+ "generation receipt logical reservation differs from its slots"
886
+ )
887
+ if reserved_unique_evaluations != expected_unique_reservation:
888
+ raise OptimizerContractError(
889
+ "generation receipt evaluation reservation differs from its slots"
890
+ )
891
+ if logical_llm_calls_after - logical_llm_calls_before != (
892
+ reserved_logical_llm_calls
893
+ ):
894
+ raise OptimizerContractError(
895
+ "generation receipt logical-call counters differ from its reservation"
896
+ )
897
+ if unique_evaluations_after - unique_evaluations_before > (
898
+ reserved_unique_evaluations
899
+ ):
900
+ raise OptimizerContractError(
901
+ "generation receipt physical evaluations exceed its reservation"
902
+ )
903
+
904
+ return {
905
+ "generation": generation,
906
+ "plan_hash": plan_hash,
907
+ "pre_archive_snapshot_hash": pre_archive_snapshot_hash,
908
+ "post_archive_snapshot_hash": post_archive_snapshot_hash,
909
+ "reward_definition_hash": reward_definition_hash,
910
+ "reward_snapshot_hash": reward_snapshot_hash,
911
+ "logical_llm_calls_before": logical_llm_calls_before,
912
+ "logical_llm_calls_after": logical_llm_calls_after,
913
+ "unique_evaluations_before": unique_evaluations_before,
914
+ "unique_evaluations_after": unique_evaluations_after,
915
+ "reserved_logical_llm_calls": reserved_logical_llm_calls,
916
+ "reserved_unique_evaluations": reserved_unique_evaluations,
917
+ "slots": [
918
+ {
919
+ "slot": _slot_record(result.slot),
920
+ "operator_invocation_id": (
921
+ result.outcome.prepared.operator_invocation_id.value
922
+ ),
923
+ "call_id": (
924
+ None
925
+ if result.outcome.prepared.call_id is None
926
+ else result.outcome.prepared.call_id.value
927
+ ),
928
+ "reserved_candidate_id": result.outcome.prepared.candidate_id.value,
929
+ "proposal_sequence": result.outcome.prepared.proposal_sequence,
930
+ "prepared_reward_definition_hash": (
931
+ result.outcome.prepared.variation_case.reward_definition_hash
932
+ ),
933
+ "prepared_selected_insights": [
934
+ {
935
+ "insight_id": reference.insight_id.value,
936
+ "version": reference.version,
937
+ }
938
+ for reference in (
939
+ result.outcome.prepared.variation_case.selected_insights
940
+ )
941
+ ],
942
+ "prepared_assignment_sha256": (
943
+ None
944
+ if result.outcome.prepared.plan.resolved_insight_assignment is None
945
+ else result.outcome.prepared.plan.resolved_insight_assignment.assignment_sha256
946
+ ),
947
+ "candidate": _candidate_identity(result.outcome.candidate),
948
+ "reward": result.outcome.reward.hex(),
949
+ "call_failure_type": result.outcome.call_failure_type,
950
+ "failure_stage": result.outcome.failure_stage,
951
+ "finite_action_decision": (
952
+ None
953
+ if result.outcome.finite_action_decision is None
954
+ else {
955
+ **result.outcome.finite_action_decision.to_record(),
956
+ "decision_sha256": (
957
+ result.outcome.finite_action_decision.decision_sha256
958
+ ),
959
+ }
960
+ ),
961
+ "treatment_admission": (
962
+ None
963
+ if result.outcome.treatment_admission_receipt is None
964
+ else {
965
+ **result.outcome.treatment_admission_receipt.to_record(),
966
+ "receipt_sha256": (
967
+ result.outcome.treatment_admission_receipt.receipt_sha256
968
+ ),
969
+ }
970
+ ),
971
+ "dominates_any_parent": result.outcome.dominates_any_parent,
972
+ "better_than_any_parent": result.outcome.better_than_any_parent,
973
+ "archive_decisions": [
974
+ decision.to_trace_record() for decision in result.archive_decisions
975
+ ],
976
+ }
977
+ for result in slot_results
978
+ ],
979
+ }
980
+
981
+
982
+ def generation_receipt_hash(receipt: GenerationReceipt) -> str:
983
+ """Recompute the canonical identity of a published generation receipt.
984
+
985
+ This is intentionally public: replay and downstream evidence adapters must
986
+ verify a receipt before trusting its nested outcomes. Frozen dataclasses
987
+ prevent in-place mutation but do not by themselves authenticate values
988
+ reconstructed from durable storage or made with ``dataclasses.replace``.
989
+ """
990
+
991
+ if type(receipt) is not GenerationReceipt:
992
+ raise TypeError("receipt must be an exact GenerationReceipt")
993
+ return _record_hash(
994
+ "generation-receipt",
995
+ _generation_receipt_record(
996
+ generation=receipt.generation,
997
+ plan_hash=receipt.plan_hash,
998
+ pre_archive_snapshot_hash=receipt.pre_archive_snapshot_hash,
999
+ post_archive_snapshot_hash=receipt.post_archive_snapshot_hash,
1000
+ reward_definition_hash=receipt.reward_definition_hash,
1001
+ reward_snapshot_hash=receipt.reward_snapshot_hash,
1002
+ logical_llm_calls_before=receipt.logical_llm_calls_before,
1003
+ logical_llm_calls_after=receipt.logical_llm_calls_after,
1004
+ unique_evaluations_before=receipt.unique_evaluations_before,
1005
+ unique_evaluations_after=receipt.unique_evaluations_after,
1006
+ reserved_logical_llm_calls=receipt.reserved_logical_llm_calls,
1007
+ reserved_unique_evaluations=receipt.reserved_unique_evaluations,
1008
+ slot_results=receipt.slot_results,
1009
+ ),
1010
+ )
1011
+
1012
+
1013
+ def validate_generation_receipt_integrity(receipt: GenerationReceipt) -> None:
1014
+ """Fail closed unless ``receipt_hash`` authenticates the exact projection."""
1015
+
1016
+ if type(receipt) is not GenerationReceipt:
1017
+ raise TypeError("receipt must be an exact GenerationReceipt")
1018
+ require_sha256(receipt.receipt_hash, "receipt_hash")
1019
+ if generation_receipt_hash(receipt) != receipt.receipt_hash:
1020
+ raise OptimizerContractError(
1021
+ "generation receipt hash does not authenticate its contents"
1022
+ )
1023
+
1024
+
1025
+ def seed_receipt_hash(receipt: SeedReceipt) -> str:
1026
+ """Recompute the canonical identity of one seed-admission receipt."""
1027
+
1028
+ if type(receipt) is not SeedReceipt:
1029
+ raise TypeError("receipt must be an exact SeedReceipt")
1030
+ record = {
1031
+ "label": receipt.label,
1032
+ "candidate": _candidate_identity(receipt.candidate),
1033
+ "candidate_objectives": [
1034
+ [name, float(value).hex()] for name, value in receipt.candidate.objectives
1035
+ ],
1036
+ "candidate_configuration_artifact_hash": (
1037
+ receipt.candidate.occurrence.configuration_artifact_hash
1038
+ ),
1039
+ "requested_configuration_hash": (
1040
+ receipt.candidate.occurrence.configuration_hash
1041
+ ),
1042
+ "gate": receipt.gate_decision.to_trace_record(),
1043
+ "archive_decision_sequences": [
1044
+ decision.decision_sequence for decision in receipt.archive_decisions
1045
+ ],
1046
+ "unique_evaluations_before": receipt.unique_evaluations_before,
1047
+ "unique_evaluations_after": receipt.unique_evaluations_after,
1048
+ "archive_snapshot_hash": receipt.archive_snapshot_hash,
1049
+ }
1050
+ if receipt.candidate.objective_resolution_receipt is not None:
1051
+ record["objective_resolution_receipt_sha256"] = (
1052
+ receipt.candidate.objective_resolution_receipt.receipt_sha256
1053
+ )
1054
+ return _record_hash("seed-receipt", record)
1055
+
1056
+
1057
+ def validate_seed_receipt_integrity(receipt: SeedReceipt) -> None:
1058
+ """Fail closed unless a seed receipt authenticates its full projection."""
1059
+
1060
+ if type(receipt) is not SeedReceipt:
1061
+ raise TypeError("receipt must be an exact SeedReceipt")
1062
+ require_sha256(receipt.receipt_hash, "receipt_hash")
1063
+ if seed_receipt_hash(receipt) != receipt.receipt_hash:
1064
+ raise OptimizerContractError(
1065
+ "seed receipt hash does not authenticate its contents"
1066
+ )
1067
+
1068
+
1069
+ def optimizer_result_hash(result: OptimizerResult) -> str:
1070
+ """Recompute the public terminal optimizer-result commitment."""
1071
+
1072
+ if type(result) is not OptimizerResult:
1073
+ raise TypeError("result must be an exact OptimizerResult")
1074
+ return _record_hash(
1075
+ "optimizer-result",
1076
+ {
1077
+ "budget_hash": result.budget.budget_hash,
1078
+ "stop_reason": result.stop_reason.value,
1079
+ "generation": result.final_state.generation,
1080
+ "unique_evaluations": result.final_state.unique_evaluations,
1081
+ "logical_llm_calls": result.final_state.logical_llm_calls,
1082
+ "archive_snapshot_hash": result.final_state.archive_snapshot_hash,
1083
+ "seed_receipt_hashes": [item.receipt_hash for item in result.seed_receipts],
1084
+ "generation_receipt_hashes": [
1085
+ item.receipt_hash for item in result.generation_receipts
1086
+ ],
1087
+ "feedback_receipt_hashes": [
1088
+ item.receipt_hash for item in result.feedback_receipts
1089
+ ],
1090
+ },
1091
+ )
1092
+
1093
+
1094
+ def validate_optimizer_result_integrity(result: OptimizerResult) -> None:
1095
+ """Authenticate a complete result and every receipt it directly owns."""
1096
+
1097
+ if type(result) is not OptimizerResult:
1098
+ raise TypeError("result must be an exact OptimizerResult")
1099
+ OptimizerState.__post_init__(result.final_state)
1100
+ if result.generation_receipts != result.final_state.generation_receipts:
1101
+ raise OptimizerContractError(
1102
+ "result generation receipts differ from its final state"
1103
+ )
1104
+ if result.feedback_receipts != result.final_state.feedback_receipts:
1105
+ raise OptimizerContractError(
1106
+ "result feedback receipts differ from its final state"
1107
+ )
1108
+ for receipt in result.seed_receipts:
1109
+ validate_seed_receipt_integrity(receipt)
1110
+ for receipt in result.generation_receipts:
1111
+ validate_generation_receipt_integrity(receipt)
1112
+ for receipt in result.feedback_receipts:
1113
+ validate_generation_feedback_receipt(receipt)
1114
+ require_sha256(result.result_hash, "result_hash")
1115
+ if optimizer_result_hash(result) != result.result_hash:
1116
+ raise OptimizerContractError(
1117
+ "optimizer result hash does not authenticate its contents"
1118
+ )
1119
+
1120
+
1121
+ class BudgetedAgenticOptimizer:
1122
+ """Compose a planner, evolution engine, and archive under hard run budgets."""
1123
+
1124
+ def __init__(
1125
+ self,
1126
+ *,
1127
+ engine: AgenticEvolutionEngine,
1128
+ archive: ParetoArchive,
1129
+ planner: GenerationPlanner,
1130
+ budget: OptimizerBudget,
1131
+ seed_admission_policy: SeedAdmissionPolicy | None = None,
1132
+ feedback_interceptor: GenerationFeedbackInterceptor | None = None,
1133
+ trace_sink: OptimizerTraceSink | None = None,
1134
+ ) -> None:
1135
+ if not isinstance(engine, AgenticEvolutionEngine):
1136
+ raise TypeError("engine must be an AgenticEvolutionEngine")
1137
+ if type(archive) is not ParetoArchive:
1138
+ raise TypeError("archive must be an exact ParetoArchive")
1139
+ if tuple(archive.objectives) != tuple(engine.objectives):
1140
+ raise ValueError("engine and archive objective contracts differ")
1141
+ if (
1142
+ archive.outcome_relation_binding.identity
1143
+ != engine.outcome_relation_binding.identity
1144
+ ):
1145
+ raise ValueError("engine and archive outcome relation bindings differ")
1146
+ if not callable(getattr(planner, "plan", None)):
1147
+ raise TypeError("planner must implement plan(state, budget)")
1148
+ if type(budget) is not OptimizerBudget:
1149
+ raise TypeError("budget must be an exact OptimizerBudget")
1150
+ if feedback_interceptor is not None and not isinstance(
1151
+ feedback_interceptor,
1152
+ GenerationFeedbackInterceptor,
1153
+ ):
1154
+ raise TypeError(
1155
+ "feedback_interceptor must implement reserve and after_generation"
1156
+ )
1157
+ if trace_sink is not None and not callable(trace_sink):
1158
+ raise TypeError("trace_sink must be callable")
1159
+ gate = (
1160
+ ValidSeedAdmissionPolicy()
1161
+ if seed_admission_policy is None
1162
+ else seed_admission_policy
1163
+ )
1164
+ if not callable(getattr(gate, "assess", None)):
1165
+ raise TypeError("seed_admission_policy must implement assess")
1166
+ self.engine = engine
1167
+ self.archive = archive
1168
+ self.planner = planner
1169
+ self.budget = budget
1170
+ self.seed_admission_policy = gate
1171
+ self.feedback_interceptor = feedback_interceptor
1172
+ self._trace_sink = trace_sink
1173
+ self._started = False
1174
+ self._trace_sequence = 0
1175
+ self._trace_origin_ns = time.monotonic_ns()
1176
+
1177
+ def _emit(self, event_type: str, **payload: object) -> None:
1178
+ if self._trace_sink is None:
1179
+ return
1180
+ self._trace_sequence += 1
1181
+ self._trace_sink(
1182
+ {
1183
+ "optimizer_trace_sequence": self._trace_sequence,
1184
+ "event_type": event_type,
1185
+ "optimizer_monotonic_offset_ns": (
1186
+ time.monotonic_ns() - self._trace_origin_ns
1187
+ ),
1188
+ **payload,
1189
+ }
1190
+ )
1191
+
1192
+ async def _evaluation_misses(self) -> int:
1193
+ snapshot = await self.engine.evaluation_cache_snapshot()
1194
+ misses = snapshot["misses"]
1195
+ in_flight = snapshot["in_flight"]
1196
+ if type(misses) is not int or type(in_flight) is not int:
1197
+ raise RuntimeError("engine evaluation cache returned invalid counters")
1198
+ if in_flight != 0:
1199
+ raise OptimizerContractError(
1200
+ "engine has evaluations in flight at an optimizer checkpoint"
1201
+ )
1202
+ return misses
1203
+
1204
+ def _state(
1205
+ self,
1206
+ *,
1207
+ generation: int,
1208
+ candidates: tuple[EvolutionCandidate, ...],
1209
+ unique_evaluations: int,
1210
+ logical_llm_calls: int,
1211
+ generation_receipts: tuple[GenerationReceipt, ...],
1212
+ feedback_receipts: tuple[GenerationFeedbackReceipt, ...],
1213
+ ) -> OptimizerState:
1214
+ snapshot = self.archive.snapshot()
1215
+ return OptimizerState(
1216
+ generation=generation,
1217
+ candidates=candidates,
1218
+ archive=snapshot,
1219
+ archive_snapshot_hash=pareto_archive_snapshot_hash(snapshot),
1220
+ unique_evaluations=unique_evaluations,
1221
+ logical_llm_calls=logical_llm_calls,
1222
+ generation_receipts=generation_receipts,
1223
+ feedback_receipts=feedback_receipts,
1224
+ )
1225
+
1226
+ def _validate_plan(self, plan: GenerationPlan, state: OptimizerState) -> None:
1227
+ if plan.generation != state.generation + 1:
1228
+ raise OptimizerContractError(
1229
+ "planner generation is not the next unpublished generation"
1230
+ )
1231
+ if plan.reward.archive_snapshot_hash != state.archive_snapshot_hash:
1232
+ raise OptimizerContractError(
1233
+ "wave reward is not bound to the exact pre-wave archive cutoff"
1234
+ )
1235
+ known = {candidate.candidate_id: candidate for candidate in state.candidates}
1236
+ materialized_ids = set()
1237
+ for slot in plan.slots:
1238
+ for parent in slot.plan.parents:
1239
+ if known.get(parent.candidate_id) != parent:
1240
+ raise OptimizerContractError(
1241
+ f"slot {slot.slot_id!r} refers to an unknown or altered parent"
1242
+ )
1243
+ ancestor = slot.plan.common_ancestor
1244
+ if ancestor is not None and known.get(ancestor.candidate_id) != ancestor:
1245
+ raise OptimizerContractError(
1246
+ f"slot {slot.slot_id!r} refers to an unknown or altered ancestor"
1247
+ )
1248
+ if slot.materialized is not None:
1249
+ candidate_id = slot.materialized.candidate_id
1250
+ if candidate_id in known or candidate_id in materialized_ids:
1251
+ raise OptimizerContractError(
1252
+ "materialized candidate occurrence IDs must be new and unique"
1253
+ )
1254
+ materialized_ids.add(candidate_id)
1255
+
1256
+ def _reserve_plan(
1257
+ self,
1258
+ plan: GenerationPlan,
1259
+ state: OptimizerState,
1260
+ feedback_reservation: GenerationFeedbackReservation | None,
1261
+ ) -> None:
1262
+ auxiliary_calls = (
1263
+ 0
1264
+ if feedback_reservation is None
1265
+ else feedback_reservation.logical_llm_calls
1266
+ )
1267
+ if (
1268
+ state.logical_llm_calls
1269
+ + plan.logical_llm_call_reservation
1270
+ + auxiliary_calls
1271
+ > self.budget.max_logical_llm_calls
1272
+ ):
1273
+ raise OptimizerBudgetExceeded(
1274
+ "generation exceeds the logical LLM-call budget"
1275
+ )
1276
+ if (
1277
+ state.unique_evaluations + plan.unique_evaluation_reservation
1278
+ > self.budget.max_unique_evaluations
1279
+ ):
1280
+ raise OptimizerBudgetExceeded(
1281
+ "generation could exceed the unique-evaluation budget"
1282
+ )
1283
+
1284
+ async def _execute_plan(
1285
+ self,
1286
+ plan: GenerationPlan,
1287
+ ) -> tuple[InvocationOutcome, ...]:
1288
+ direct_slots = tuple(
1289
+ slot
1290
+ for slot in plan.slots
1291
+ if slot.proposal_authority is not ProposalAuthority.ENGINE
1292
+ )
1293
+ engine_slots = tuple(
1294
+ slot
1295
+ for slot in plan.slots
1296
+ if slot.proposal_authority is ProposalAuthority.ENGINE
1297
+ )
1298
+
1299
+ async def direct() -> tuple[InvocationOutcome, ...]:
1300
+ if not direct_slots:
1301
+ return ()
1302
+ return await self.engine.run_invocations(
1303
+ tuple(slot.plan for slot in direct_slots),
1304
+ reward_binding=plan.reward.binding,
1305
+ )
1306
+
1307
+ async def materialized() -> tuple[InvocationOutcome, ...]:
1308
+ if not engine_slots:
1309
+ return ()
1310
+ return await self.engine.run_materialized_invocations(
1311
+ tuple(slot.materialized for slot in engine_slots), # type: ignore[arg-type]
1312
+ reward_binding=plan.reward.binding,
1313
+ )
1314
+
1315
+ direct_outcomes, engine_outcomes = await asyncio.gather(
1316
+ direct(), materialized()
1317
+ )
1318
+ by_slot: dict[str, InvocationOutcome] = {}
1319
+ for slot, outcome in zip(direct_slots, direct_outcomes, strict=True):
1320
+ by_slot[slot.slot_id] = outcome
1321
+ for slot, outcome in zip(engine_slots, engine_outcomes, strict=True):
1322
+ by_slot[slot.slot_id] = outcome
1323
+ if set(by_slot) != {slot.slot_id for slot in plan.slots}:
1324
+ raise RuntimeError("engine returned an incomplete generation")
1325
+ ordered = tuple(by_slot[slot.slot_id] for slot in plan.slots)
1326
+ for slot, outcome in zip(plan.slots, ordered, strict=True):
1327
+ prepared = outcome.prepared
1328
+ if prepared.plan != slot.plan:
1329
+ raise RuntimeError("engine outcome differs from its admitted slot plan")
1330
+ if prepared.proposal_authority is not slot.proposal_authority:
1331
+ raise RuntimeError("engine outcome has the wrong proposal authority")
1332
+ if (
1333
+ prepared.variation_case.reward_definition_hash
1334
+ != plan.reward.binding.definition_hash
1335
+ ):
1336
+ raise RuntimeError("engine outcome has the wrong reward identity")
1337
+ if not math.isfinite(outcome.reward):
1338
+ raise RuntimeError("engine outcome reward must be finite")
1339
+ return ordered
1340
+
1341
+ async def run(
1342
+ self,
1343
+ seed_configs: Sequence[dict[str, object]],
1344
+ ) -> OptimizerResult:
1345
+ """Evaluate seeds and execute exactly ``max_generations`` planner waves."""
1346
+
1347
+ if self._started:
1348
+ raise OptimizerContractError("an optimizer instance is single-use")
1349
+ self._started = True
1350
+ if self.archive.snapshot().consideration_count != 0:
1351
+ raise OptimizerContractError("optimizer requires a fresh empty archive")
1352
+ seeds = tuple(seed_configs)
1353
+ if not seeds:
1354
+ raise OptimizerContractError("at least one seed configuration is required")
1355
+ if any(type(config) is not dict for config in seeds):
1356
+ raise TypeError("seed configurations must be exact dictionaries")
1357
+ seed_hashes = tuple(typed_json_sha256(freeze_json(config)) for config in seeds)
1358
+ if len(set(seed_hashes)) != len(seed_hashes):
1359
+ raise OptimizerContractError("seed configurations must be unique")
1360
+ if len(seeds) > self.budget.max_unique_evaluations:
1361
+ raise OptimizerBudgetExceeded(
1362
+ "seed gate could exceed the unique-evaluation budget"
1363
+ )
1364
+
1365
+ initial_misses = await self._evaluation_misses()
1366
+ self._emit(
1367
+ "optimizer_started",
1368
+ budget=self.budget.to_trace_record(),
1369
+ budget_hash=self.budget.budget_hash,
1370
+ initial_engine_evaluation_misses=initial_misses,
1371
+ seed_configuration_hashes=list(seed_hashes),
1372
+ )
1373
+ candidates: tuple[EvolutionCandidate, ...] = ()
1374
+ seed_receipts: list[SeedReceipt] = []
1375
+ prior_unique = 0
1376
+ for index, config in enumerate(seeds):
1377
+ label = f"seed_{index}"
1378
+ candidate = await self.engine.register_seed(config, label=label)
1379
+ current_unique = (await self._evaluation_misses()) - initial_misses
1380
+ if not 0 <= current_unique <= self.budget.max_unique_evaluations:
1381
+ raise OptimizerExecutionError(
1382
+ "seed evaluation counters exceeded budget"
1383
+ )
1384
+ gate_context = SeedGateContext(
1385
+ seed_index=index,
1386
+ label=label,
1387
+ requested_configuration_hash=seed_hashes[index],
1388
+ unique_evaluations_before=prior_unique,
1389
+ unique_evaluations_after=current_unique,
1390
+ )
1391
+ try:
1392
+ gate_decision = self.seed_admission_policy.assess(
1393
+ candidate,
1394
+ gate_context,
1395
+ )
1396
+ except Exception as exc:
1397
+ self._emit(
1398
+ "optimizer_seed_gate_failed",
1399
+ label=label,
1400
+ candidate=_candidate_identity(candidate),
1401
+ requested_configuration_hash=seed_hashes[index],
1402
+ unique_evaluations_before=prior_unique,
1403
+ unique_evaluations_after=current_unique,
1404
+ failure_type=type(exc).__name__,
1405
+ )
1406
+ raise OptimizerExecutionError(
1407
+ f"seed admission policy failed for {label}"
1408
+ ) from exc
1409
+ if type(gate_decision) is not SeedGateDecision:
1410
+ raise OptimizerContractError(
1411
+ "seed admission policy must return an exact SeedGateDecision"
1412
+ )
1413
+ SeedGateDecision.__post_init__(gate_decision)
1414
+ if gate_decision.admitted and not candidate.valid:
1415
+ raise OptimizerContractError(
1416
+ "a seed admission policy cannot override engine invalidity"
1417
+ )
1418
+ decisions = (
1419
+ self.archive.consider(candidate) if gate_decision.admitted else ()
1420
+ )
1421
+ snapshot_hash = pareto_archive_snapshot_hash(self.archive.snapshot())
1422
+ seed_record = {
1423
+ "label": label,
1424
+ "candidate": _candidate_identity(candidate),
1425
+ "candidate_objectives": [
1426
+ [name, float(value).hex()] for name, value in candidate.objectives
1427
+ ],
1428
+ "candidate_configuration_artifact_hash": (
1429
+ candidate.occurrence.configuration_artifact_hash
1430
+ ),
1431
+ "requested_configuration_hash": seed_hashes[index],
1432
+ "gate": gate_decision.to_trace_record(),
1433
+ "archive_decision_sequences": [
1434
+ decision.decision_sequence for decision in decisions
1435
+ ],
1436
+ "unique_evaluations_before": prior_unique,
1437
+ "unique_evaluations_after": current_unique,
1438
+ "archive_snapshot_hash": snapshot_hash,
1439
+ }
1440
+ if candidate.objective_resolution_receipt is not None:
1441
+ seed_record["objective_resolution_receipt_sha256"] = (
1442
+ candidate.objective_resolution_receipt.receipt_sha256
1443
+ )
1444
+ receipt = SeedReceipt(
1445
+ label=label,
1446
+ candidate=candidate,
1447
+ gate_decision=gate_decision,
1448
+ archive_decisions=decisions,
1449
+ unique_evaluations_before=prior_unique,
1450
+ unique_evaluations_after=current_unique,
1451
+ archive_snapshot_hash=snapshot_hash,
1452
+ receipt_hash=_record_hash("seed-receipt", seed_record),
1453
+ )
1454
+ seed_receipts.append(receipt)
1455
+ candidates = (*candidates, candidate)
1456
+ prior_unique = current_unique
1457
+ self._emit(
1458
+ "optimizer_seed_completed",
1459
+ **seed_record,
1460
+ receipt_hash=receipt.receipt_hash,
1461
+ valid=candidate.valid,
1462
+ )
1463
+ if not gate_decision.admitted:
1464
+ raise OptimizerExecutionError(
1465
+ "seed gate rejected candidate "
1466
+ f"{candidate.candidate_id.value}: {gate_decision.reason}"
1467
+ )
1468
+
1469
+ state = self._state(
1470
+ generation=0,
1471
+ candidates=candidates,
1472
+ unique_evaluations=prior_unique,
1473
+ logical_llm_calls=0,
1474
+ generation_receipts=(),
1475
+ feedback_receipts=(),
1476
+ )
1477
+ generation_receipts: list[GenerationReceipt] = []
1478
+ feedback_receipts: list[GenerationFeedbackReceipt] = []
1479
+
1480
+ while state.generation < self.budget.max_generations:
1481
+ try:
1482
+ plan = self.planner.plan(state, self.budget)
1483
+ except Exception as exc:
1484
+ self._emit(
1485
+ "optimizer_planning_failed",
1486
+ generation=state.generation + 1,
1487
+ failure_type=type(exc).__name__,
1488
+ )
1489
+ raise OptimizerPlanningError(
1490
+ f"planner failed for generation {state.generation + 1}"
1491
+ ) from exc
1492
+ if type(plan) is not GenerationPlan:
1493
+ raise OptimizerContractError(
1494
+ "planner must return an exact GenerationPlan"
1495
+ )
1496
+ GenerationPlan.__post_init__(plan)
1497
+ self._validate_plan(plan, state)
1498
+ feedback_reservation: GenerationFeedbackReservation | None = None
1499
+ if self.feedback_interceptor is not None:
1500
+ try:
1501
+ feedback_reservation = self.feedback_interceptor.reserve(
1502
+ state=state,
1503
+ plan=plan,
1504
+ )
1505
+ except Exception as exc:
1506
+ self._emit(
1507
+ "optimizer_generation_feedback_reservation_failed",
1508
+ generation=plan.generation,
1509
+ failure_type=type(exc).__name__,
1510
+ )
1511
+ raise OptimizerPlanningError(
1512
+ "generation feedback reservation failed for "
1513
+ f"generation {plan.generation}"
1514
+ ) from exc
1515
+ if type(feedback_reservation) is not GenerationFeedbackReservation:
1516
+ raise OptimizerContractError(
1517
+ "feedback interceptor must return an exact reservation"
1518
+ )
1519
+ GenerationFeedbackReservation.__post_init__(feedback_reservation)
1520
+ plan_record = _generation_plan_record(
1521
+ plan,
1522
+ budget_hash=self.budget.budget_hash,
1523
+ )
1524
+ plan_hash = _record_hash("generation-plan", plan_record)
1525
+ try:
1526
+ self._reserve_plan(plan, state, feedback_reservation)
1527
+ except OptimizerBudgetExceeded:
1528
+ self._emit(
1529
+ "optimizer_generation_rejected",
1530
+ plan_hash=plan_hash,
1531
+ feedback_reservation=(
1532
+ None
1533
+ if feedback_reservation is None
1534
+ else {
1535
+ **feedback_reservation.to_record(),
1536
+ "reservation_hash": (feedback_reservation.reservation_hash),
1537
+ }
1538
+ ),
1539
+ **plan_record,
1540
+ )
1541
+ raise
1542
+ if feedback_reservation is not None:
1543
+ self._emit(
1544
+ "optimizer_generation_feedback_reserved",
1545
+ generation=plan.generation,
1546
+ plan_hash=plan_hash,
1547
+ **feedback_reservation.to_record(),
1548
+ reservation_hash=feedback_reservation.reservation_hash,
1549
+ )
1550
+ self._emit(
1551
+ "optimizer_generation_planned",
1552
+ plan_hash=plan_hash,
1553
+ feedback_reservation_hash=(
1554
+ None
1555
+ if feedback_reservation is None
1556
+ else feedback_reservation.reservation_hash
1557
+ ),
1558
+ reserved_feedback_logical_llm_calls=(
1559
+ 0
1560
+ if feedback_reservation is None
1561
+ else feedback_reservation.logical_llm_calls
1562
+ ),
1563
+ **plan_record,
1564
+ )
1565
+ if (
1566
+ _record_hash(
1567
+ "generation-plan",
1568
+ _generation_plan_record(
1569
+ plan,
1570
+ budget_hash=self.budget.budget_hash,
1571
+ ),
1572
+ )
1573
+ != plan_hash
1574
+ ):
1575
+ raise OptimizerContractError(
1576
+ "generation plan changed after its admission receipt"
1577
+ )
1578
+
1579
+ before_unique = state.unique_evaluations
1580
+ before_calls = state.logical_llm_calls
1581
+ try:
1582
+ outcomes = await self._execute_plan(plan)
1583
+ except asyncio.CancelledError:
1584
+ raise
1585
+ except Exception as exc:
1586
+ self._emit(
1587
+ "optimizer_generation_execution_failed",
1588
+ generation=plan.generation,
1589
+ plan_hash=plan_hash,
1590
+ failure_type=type(exc).__name__,
1591
+ )
1592
+ raise OptimizerExecutionError(
1593
+ f"generation {plan.generation} execution failed"
1594
+ ) from exc
1595
+ if (
1596
+ _record_hash(
1597
+ "generation-plan",
1598
+ _generation_plan_record(
1599
+ plan,
1600
+ budget_hash=self.budget.budget_hash,
1601
+ ),
1602
+ )
1603
+ != plan_hash
1604
+ ):
1605
+ raise OptimizerExecutionError(
1606
+ "generation plan changed while its slots were executing"
1607
+ )
1608
+
1609
+ observed_calls = sum(
1610
+ outcome.prepared.call_id is not None for outcome in outcomes
1611
+ )
1612
+ if observed_calls != plan.logical_llm_call_reservation:
1613
+ raise OptimizerExecutionError(
1614
+ "engine logical-call identities differ from reservations"
1615
+ )
1616
+ after_calls = before_calls + observed_calls
1617
+ after_unique = (await self._evaluation_misses()) - initial_misses
1618
+ if (
1619
+ after_calls > self.budget.max_logical_llm_calls
1620
+ or after_unique > self.budget.max_unique_evaluations
1621
+ or after_unique < before_unique
1622
+ ):
1623
+ raise OptimizerExecutionError("engine counters violated hard budgets")
1624
+
1625
+ slot_results: list[SlotResult] = []
1626
+ next_candidates = list(state.candidates)
1627
+ for slot, outcome in zip(plan.slots, outcomes, strict=True):
1628
+ candidate = outcome.candidate
1629
+ decisions: tuple[ParetoDecision, ...] = ()
1630
+ if candidate is not None:
1631
+ if any(
1632
+ existing.candidate_id == candidate.candidate_id
1633
+ for existing in next_candidates
1634
+ ):
1635
+ raise OptimizerExecutionError(
1636
+ "engine reused a candidate occurrence ID"
1637
+ )
1638
+ next_candidates.append(candidate)
1639
+ decisions = self.archive.consider(candidate)
1640
+ slot_results.append(SlotResult(slot, outcome, decisions))
1641
+
1642
+ post_snapshot = self.archive.snapshot()
1643
+ post_archive_hash = pareto_archive_snapshot_hash(post_snapshot)
1644
+ receipt_record = _generation_receipt_record(
1645
+ generation=plan.generation,
1646
+ plan_hash=plan_hash,
1647
+ pre_archive_snapshot_hash=state.archive_snapshot_hash,
1648
+ post_archive_snapshot_hash=post_archive_hash,
1649
+ reward_definition_hash=plan.reward.binding.definition_hash,
1650
+ reward_snapshot_hash=plan.reward.reward_snapshot_hash,
1651
+ logical_llm_calls_before=before_calls,
1652
+ logical_llm_calls_after=after_calls,
1653
+ unique_evaluations_before=before_unique,
1654
+ unique_evaluations_after=after_unique,
1655
+ reserved_logical_llm_calls=plan.logical_llm_call_reservation,
1656
+ reserved_unique_evaluations=plan.unique_evaluation_reservation,
1657
+ slot_results=tuple(slot_results),
1658
+ )
1659
+ receipt_hash = _record_hash("generation-receipt", receipt_record)
1660
+ receipt = GenerationReceipt(
1661
+ generation=plan.generation,
1662
+ plan_hash=plan_hash,
1663
+ pre_archive_snapshot_hash=state.archive_snapshot_hash,
1664
+ post_archive_snapshot_hash=post_archive_hash,
1665
+ reward_definition_hash=plan.reward.binding.definition_hash,
1666
+ reward_snapshot_hash=plan.reward.reward_snapshot_hash,
1667
+ logical_llm_calls_before=before_calls,
1668
+ logical_llm_calls_after=after_calls,
1669
+ unique_evaluations_before=before_unique,
1670
+ unique_evaluations_after=after_unique,
1671
+ reserved_logical_llm_calls=plan.logical_llm_call_reservation,
1672
+ reserved_unique_evaluations=plan.unique_evaluation_reservation,
1673
+ slot_results=tuple(slot_results),
1674
+ receipt_hash=receipt_hash,
1675
+ )
1676
+ generation_receipts.append(receipt)
1677
+ self._emit(
1678
+ "optimizer_generation_completed",
1679
+ **receipt_record,
1680
+ receipt_hash=receipt_hash,
1681
+ )
1682
+ state = self._state(
1683
+ generation=plan.generation,
1684
+ candidates=tuple(next_candidates),
1685
+ unique_evaluations=after_unique,
1686
+ logical_llm_calls=after_calls,
1687
+ generation_receipts=tuple(generation_receipts),
1688
+ feedback_receipts=tuple(feedback_receipts),
1689
+ )
1690
+ if self.feedback_interceptor is not None:
1691
+ assert feedback_reservation is not None
1692
+ context = GenerationFeedbackContext(
1693
+ state=state,
1694
+ plan=plan,
1695
+ generation_receipt=receipt,
1696
+ reservation=feedback_reservation,
1697
+ )
1698
+ self._emit(
1699
+ "optimizer_generation_feedback_started",
1700
+ generation=plan.generation,
1701
+ generation_receipt_hash=receipt.receipt_hash,
1702
+ reservation_hash=feedback_reservation.reservation_hash,
1703
+ )
1704
+ try:
1705
+ feedback_result = await self.feedback_interceptor.after_generation(
1706
+ context
1707
+ )
1708
+ except asyncio.CancelledError:
1709
+ raise
1710
+ except Exception as exc:
1711
+ self._emit(
1712
+ "optimizer_generation_feedback_failed",
1713
+ generation=plan.generation,
1714
+ generation_receipt_hash=receipt.receipt_hash,
1715
+ reservation_hash=feedback_reservation.reservation_hash,
1716
+ failure_type=type(exc).__name__,
1717
+ )
1718
+ raise OptimizerExecutionError(
1719
+ f"generation feedback failed for generation {plan.generation}"
1720
+ ) from exc
1721
+ if type(feedback_result) is not GenerationFeedbackResult:
1722
+ raise OptimizerContractError(
1723
+ "feedback interceptor must return an exact result"
1724
+ )
1725
+ try:
1726
+ feedback_receipt = seal_generation_feedback(
1727
+ context=context,
1728
+ result=feedback_result,
1729
+ )
1730
+ except (TypeError, ValueError) as exc:
1731
+ raise OptimizerContractError(
1732
+ "feedback result differs from its admitted reservation"
1733
+ ) from exc
1734
+ if (
1735
+ feedback_receipt.logical_llm_calls_after
1736
+ > self.budget.max_logical_llm_calls
1737
+ ):
1738
+ raise OptimizerExecutionError(
1739
+ "feedback logical-call counters violated the hard budget"
1740
+ )
1741
+ feedback_receipts.append(feedback_receipt)
1742
+ self._emit(
1743
+ "optimizer_generation_feedback_completed",
1744
+ generation=plan.generation,
1745
+ policy_id=feedback_receipt.policy_id,
1746
+ policy_version=feedback_receipt.policy_version,
1747
+ generation_receipt_hash=receipt.receipt_hash,
1748
+ reservation_hash=feedback_receipt.reservation_hash,
1749
+ reserved_logical_llm_calls=(
1750
+ feedback_receipt.reserved_logical_llm_calls
1751
+ ),
1752
+ used_logical_llm_calls=feedback_receipt.used_logical_llm_calls,
1753
+ logical_llm_calls_before=(
1754
+ feedback_receipt.logical_llm_calls_before
1755
+ ),
1756
+ logical_llm_calls_after=(feedback_receipt.logical_llm_calls_after),
1757
+ result_metadata=[
1758
+ list(item) for item in feedback_receipt.result_metadata
1759
+ ],
1760
+ feedback_receipt_hash=feedback_receipt.receipt_hash,
1761
+ )
1762
+ state = self._state(
1763
+ generation=plan.generation,
1764
+ candidates=tuple(next_candidates),
1765
+ unique_evaluations=after_unique,
1766
+ logical_llm_calls=(feedback_receipt.logical_llm_calls_after),
1767
+ generation_receipts=tuple(generation_receipts),
1768
+ feedback_receipts=tuple(feedback_receipts),
1769
+ )
1770
+
1771
+ stop_reason = OptimizerStopReason.GENERATION_LIMIT_REACHED
1772
+ result_record = {
1773
+ "budget_hash": self.budget.budget_hash,
1774
+ "stop_reason": stop_reason.value,
1775
+ "generation": state.generation,
1776
+ "unique_evaluations": state.unique_evaluations,
1777
+ "logical_llm_calls": state.logical_llm_calls,
1778
+ "archive_snapshot_hash": state.archive_snapshot_hash,
1779
+ "seed_receipt_hashes": [item.receipt_hash for item in seed_receipts],
1780
+ "generation_receipt_hashes": [
1781
+ item.receipt_hash for item in generation_receipts
1782
+ ],
1783
+ "feedback_receipt_hashes": [
1784
+ item.receipt_hash for item in feedback_receipts
1785
+ ],
1786
+ }
1787
+ result_hash = _record_hash("optimizer-result", result_record)
1788
+ self._emit("optimizer_completed", **result_record, result_hash=result_hash)
1789
+ return OptimizerResult(
1790
+ budget=self.budget,
1791
+ final_state=state,
1792
+ seed_receipts=tuple(seed_receipts),
1793
+ generation_receipts=tuple(generation_receipts),
1794
+ feedback_receipts=tuple(feedback_receipts),
1795
+ stop_reason=stop_reason,
1796
+ result_hash=result_hash,
1797
+ )
1798
+
1799
+
1800
+ __all__ = [
1801
+ "BudgetedAgenticOptimizer",
1802
+ "FrozenWaveReward",
1803
+ "GenerationPlan",
1804
+ "GenerationPlanner",
1805
+ "GenerationReceipt",
1806
+ "OptimizerBudget",
1807
+ "OptimizerBudgetExceeded",
1808
+ "OptimizerContractError",
1809
+ "OptimizerExecutionError",
1810
+ "OptimizerPlanningError",
1811
+ "OptimizerResult",
1812
+ "OptimizerSlot",
1813
+ "OptimizerState",
1814
+ "OptimizerStopReason",
1815
+ "SeedReceipt",
1816
+ "SeedAdmissionPolicy",
1817
+ "SeedGateContext",
1818
+ "SeedGateDecision",
1819
+ "SlotResult",
1820
+ "ValidSeedAdmissionPolicy",
1821
+ "generation_receipt_hash",
1822
+ "optimizer_result_hash",
1823
+ "pareto_archive_snapshot_hash",
1824
+ "seed_receipt_hash",
1825
+ "validate_generation_receipt_integrity",
1826
+ "validate_optimizer_result_integrity",
1827
+ "validate_seed_receipt_integrity",
1828
+ ]