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,1840 @@
1
+ """Workload-neutral preparation boundary for multi-generation campaigns.
2
+
3
+ This module deliberately stops before live evolutionary execution. It seals
4
+ the shared chronology, validates budgets and concurrency against a benchmark
5
+ preflight, loads an exact seed batch, asks an injected agent runtime to
6
+ preflight the composition, and journals the resulting immutable preparation
7
+ record. Existing portfolio-evolution and recombination services remain the
8
+ authorities for executing their respective waves.
9
+
10
+ Benchmark adapters enter only through inverted ports. Model and provider
11
+ configuration belongs to ``CampaignAgentRuntimePort`` implementations and is
12
+ therefore absent from campaign protocols and workload ports.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import hashlib
18
+ import json
19
+ import math
20
+ import re
21
+ from dataclasses import dataclass, field
22
+ from enum import Enum
23
+ from typing import Protocol, runtime_checkable
24
+
25
+ from agent_evolve.application.budgeted_optimizer import OptimizerBudget
26
+ from agent_evolve.application.finite_variation_eligibility import (
27
+ FiniteVariationEligibilityReceipt,
28
+ )
29
+ from agent_evolve.domain.finite_variation import (
30
+ FiniteVariationContract,
31
+ validate_finite_variation_contract,
32
+ )
33
+ from agent_evolve.domain.patch import require_sha256
34
+ from agent_evolve.domain.typed_json import (
35
+ FrozenJsonObject,
36
+ freeze_json,
37
+ thaw_json,
38
+ typed_json_sha256,
39
+ )
40
+
41
+
42
+ MIN_CAMPAIGN_GENERATIONS = 3
43
+ MAX_CAMPAIGN_GENERATIONS = 24
44
+
45
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,127}$")
46
+ _PROTOCOL_DOMAIN = b"agent-evolve:evolution-campaign-protocol:v1\x00"
47
+ _SCHEDULE_DOMAIN = b"agent-evolve:evolution-campaign-schedule:v1\x00"
48
+ _SESSION_REQUEST_DOMAIN = b"agent-evolve:campaign-session-request:v1\x00"
49
+ _SESSION_DOMAIN = b"agent-evolve:campaign-benchmark-session:v1\x00"
50
+ _SEED_DOMAIN = b"agent-evolve:campaign-seed:v1\x00"
51
+ _SEED_BATCH_DOMAIN = b"agent-evolve:campaign-seed-batch:v1\x00"
52
+ _ARCHIVE_UTILITY_SNAPSHOT_DOMAIN = (
53
+ b"agent-evolve:campaign-archive-utility-snapshot:v1\x00"
54
+ )
55
+ _POLICIES_DOMAIN = b"agent-evolve:campaign-policies:v1\x00"
56
+ _REFLECTION_SUPERVISION_CONFIGURATION_DOMAIN = (
57
+ b"agent-evolve:campaign-reflection-supervision-configuration:v1\x00"
58
+ )
59
+ _WORKLOAD_PORTS_DOMAIN = b"agent-evolve:campaign-workload-ports:v1\x00"
60
+ _CONCURRENCY_DOMAIN = b"agent-evolve:campaign-concurrency:v1\x00"
61
+ _RUNTIME_REQUEST_DOMAIN = b"agent-evolve:campaign-runtime-request:v1\x00"
62
+ _RUNTIME_RECEIPT_DOMAIN = b"agent-evolve:campaign-runtime-receipt:v1\x00"
63
+ _PREPARATION_DOMAIN = b"agent-evolve:campaign-preparation:v1\x00"
64
+
65
+ ALTERNATING_CADENCE_POLICY_ID = "alternating_portfolio_recombination"
66
+ ALTERNATING_CADENCE_POLICY_VERSION = 3
67
+ ALTERNATING_CADENCE_DEFINITION_SHA256 = hashlib.sha256(
68
+ b"agent-evolve:alternating-portfolio-recombination:v3;"
69
+ b"actual-generations-not-cycles;portfolio-first;recombination-second;"
70
+ b"odd-terminal-portfolio-permitted;recombination-source-is-prior-portfolio;"
71
+ b"protocol-bound-terminal-reflection-consumer-requirement;"
72
+ b"future-consumer-requires-complete-scheduled-admission-barrier"
73
+ ).hexdigest()
74
+
75
+ SEALED_CUTOFF_DELAYED_CADENCE_POLICY_ID = (
76
+ "sealed_cutoff_delayed_portfolio_recombination"
77
+ )
78
+ SEALED_CUTOFF_DELAYED_CADENCE_POLICY_VERSION = 1
79
+ SEALED_CUTOFF_DELAYED_CADENCE_DEFINITION_SHA256 = hashlib.sha256(
80
+ b"agent-evolve:sealed-cutoff-delayed-portfolio-recombination:v1;"
81
+ b"actual-generations-not-cycles;portfolio-first;recombination-second;"
82
+ b"reflection-input-is-exact-sealed-source-stage;"
83
+ b"one-complete-pair-delay;admit-after-next-recombination-seal;"
84
+ b"first-consumer-is-following-portfolio;future-content-not-visible-to-reflector;"
85
+ b"no-online-reflection-without-scheduled-future-portfolio-consumer"
86
+ ).hexdigest()
87
+
88
+
89
+ def _canonical_json(value: object) -> bytes:
90
+ return json.dumps(
91
+ value,
92
+ ensure_ascii=True,
93
+ allow_nan=False,
94
+ separators=(",", ":"),
95
+ sort_keys=True,
96
+ ).encode("ascii", errors="strict")
97
+
98
+
99
+ def _hash(domain: bytes, record: object) -> str:
100
+ return hashlib.sha256(domain + _canonical_json(record)).hexdigest()
101
+
102
+
103
+ def _validate_token(value: str, *, name: str) -> None:
104
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
105
+ raise ValueError(f"{name} must use the closed campaign-token grammar")
106
+
107
+
108
+ def _validate_frozen_object(value: object, *, name: str) -> FrozenJsonObject:
109
+ if type(value) is not FrozenJsonObject:
110
+ raise TypeError(f"{name} must be an exact FrozenJsonObject")
111
+ if freeze_json(value) is not value:
112
+ raise TypeError(f"{name} must already be frozen typed JSON")
113
+ return value
114
+
115
+
116
+ class TerminalReflectionPolicy(str, Enum):
117
+ """Whether online reflection may run without a later portfolio consumer."""
118
+
119
+ ALLOW_TERMINAL = "allow_terminal"
120
+ REQUIRE_FUTURE_PORTFOLIO_CONSUMER = "require_future_portfolio_consumer"
121
+
122
+
123
+ @dataclass(frozen=True, slots=True)
124
+ class CampaignProtocol:
125
+ """Provider- and workload-free behavioral protocol for one campaign.
126
+
127
+ ``generation_count`` is the number of actual evolutionary generations,
128
+ not a number of portfolio/recombination cycles. Consequently a count of
129
+ three means portfolio, recombination, portfolio.
130
+ """
131
+
132
+ protocol_id: str
133
+ protocol_version: int
134
+ definition_sha256: str
135
+ outer_seed: int
136
+ generation_count: int
137
+ required_seed_count: int
138
+ parents_per_portfolio_generation: int
139
+ portfolio_width: int
140
+ recombinations_per_parent: int
141
+ reflections_per_recombination_generation: int = 1
142
+ reflection_promotion_block_pairs: int = 3
143
+ terminal_reflection_policy: TerminalReflectionPolicy = (
144
+ TerminalReflectionPolicy.ALLOW_TERMINAL
145
+ )
146
+
147
+ def __post_init__(self) -> None:
148
+ _validate_token(self.protocol_id, name="protocol_id")
149
+ if type(self.protocol_version) is not int or self.protocol_version <= 0:
150
+ raise ValueError("protocol_version must be a positive exact integer")
151
+ require_sha256(self.definition_sha256, "definition_sha256")
152
+ if type(self.outer_seed) is not int or not -(1 << 127) <= self.outer_seed < (
153
+ 1 << 127
154
+ ):
155
+ raise ValueError("outer_seed must be an exact signed int128")
156
+ if type(self.generation_count) is not int or not (
157
+ MIN_CAMPAIGN_GENERATIONS
158
+ <= self.generation_count
159
+ <= MAX_CAMPAIGN_GENERATIONS
160
+ ):
161
+ raise ValueError(
162
+ "generation_count must count 3 through 24 actual generations"
163
+ )
164
+ for name in (
165
+ "required_seed_count",
166
+ "parents_per_portfolio_generation",
167
+ "recombinations_per_parent",
168
+ ):
169
+ value = getattr(self, name)
170
+ if type(value) is not int or value <= 0:
171
+ raise ValueError(f"{name} must be a positive exact integer")
172
+ if type(self.portfolio_width) is not int or self.portfolio_width < 2:
173
+ raise ValueError("portfolio_width must be an exact integer of at least two")
174
+ if (
175
+ type(self.reflections_per_recombination_generation) is not int
176
+ or self.reflections_per_recombination_generation < 0
177
+ ):
178
+ raise ValueError(
179
+ "reflections_per_recombination_generation must be a "
180
+ "non-negative exact integer"
181
+ )
182
+ if (
183
+ type(self.reflection_promotion_block_pairs) is not int
184
+ or self.reflection_promotion_block_pairs <= 0
185
+ ):
186
+ raise ValueError(
187
+ "reflection_promotion_block_pairs must be a positive exact integer"
188
+ )
189
+ if type(self.terminal_reflection_policy) is not TerminalReflectionPolicy:
190
+ raise TypeError(
191
+ "terminal_reflection_policy must be a TerminalReflectionPolicy"
192
+ )
193
+
194
+ def to_record(self) -> dict[str, object]:
195
+ self.__post_init__()
196
+ return {
197
+ "schema_version": 1,
198
+ "protocol_id": self.protocol_id,
199
+ "protocol_version": self.protocol_version,
200
+ "definition_sha256": self.definition_sha256,
201
+ "outer_seed": self.outer_seed,
202
+ "generation_count": self.generation_count,
203
+ "required_seed_count": self.required_seed_count,
204
+ "parents_per_portfolio_generation": (self.parents_per_portfolio_generation),
205
+ "portfolio_width": self.portfolio_width,
206
+ "recombinations_per_parent": self.recombinations_per_parent,
207
+ "reflections_per_recombination_generation": (
208
+ self.reflections_per_recombination_generation
209
+ ),
210
+ "reflection_promotion_block_pairs": (self.reflection_promotion_block_pairs),
211
+ "terminal_reflection_policy": self.terminal_reflection_policy.value,
212
+ }
213
+
214
+ @property
215
+ def protocol_sha256(self) -> str:
216
+ return _hash(_PROTOCOL_DOMAIN, self.to_record())
217
+
218
+
219
+ class CampaignGenerationKind(str, Enum):
220
+ PORTFOLIO = "portfolio"
221
+ RECOMBINATION = "recombination"
222
+
223
+
224
+ @dataclass(frozen=True, slots=True)
225
+ class CampaignGenerationStep:
226
+ """One exact generation in a campaign cadence."""
227
+
228
+ generation: int
229
+ kind: CampaignGenerationKind
230
+ source_portfolio_generation: int | None
231
+ parent_count: int
232
+ offspring_per_parent: int
233
+ planned_agent_calls: int
234
+
235
+ def __post_init__(self) -> None:
236
+ if type(self.generation) is not int or self.generation <= 0:
237
+ raise ValueError("generation must be a positive exact integer")
238
+ if type(self.kind) is not CampaignGenerationKind:
239
+ raise TypeError("kind must be a CampaignGenerationKind")
240
+ for name in ("parent_count", "offspring_per_parent"):
241
+ value = getattr(self, name)
242
+ if type(value) is not int or value <= 0:
243
+ raise ValueError(f"{name} must be a positive exact integer")
244
+ if type(self.planned_agent_calls) is not int or self.planned_agent_calls < 0:
245
+ raise ValueError("planned_agent_calls must be non-negative")
246
+ if self.kind is CampaignGenerationKind.PORTFOLIO:
247
+ if self.source_portfolio_generation is not None:
248
+ raise ValueError(
249
+ "a portfolio generation cannot name a source portfolio"
250
+ )
251
+ if self.planned_agent_calls != self.parent_count:
252
+ raise ValueError(
253
+ "a portfolio generation requires one agent call per parent"
254
+ )
255
+ else:
256
+ if (
257
+ type(self.source_portfolio_generation) is not int
258
+ or self.source_portfolio_generation <= 0
259
+ or self.source_portfolio_generation >= self.generation
260
+ ):
261
+ raise ValueError(
262
+ "a recombination generation must name an earlier portfolio"
263
+ )
264
+
265
+ @property
266
+ def planned_candidate_evaluations(self) -> int:
267
+ return self.parent_count * self.offspring_per_parent
268
+
269
+ def to_record(self) -> dict[str, object]:
270
+ self.__post_init__()
271
+ return {
272
+ "generation": self.generation,
273
+ "kind": self.kind.value,
274
+ "source_portfolio_generation": self.source_portfolio_generation,
275
+ "parent_count": self.parent_count,
276
+ "offspring_per_parent": self.offspring_per_parent,
277
+ "planned_candidate_evaluations": self.planned_candidate_evaluations,
278
+ "planned_agent_calls": self.planned_agent_calls,
279
+ }
280
+
281
+
282
+ @dataclass(frozen=True, slots=True)
283
+ class CampaignGenerationPair:
284
+ """One portfolio generation and its immediately paired recombination."""
285
+
286
+ portfolio_generation: int
287
+ recombination_generation: int
288
+
289
+ def __post_init__(self) -> None:
290
+ if type(self.portfolio_generation) is not int or self.portfolio_generation <= 0:
291
+ raise ValueError("portfolio_generation must be positive")
292
+ if self.recombination_generation != self.portfolio_generation + 1:
293
+ raise ValueError("recombination must immediately follow its portfolio")
294
+
295
+ def to_record(self) -> dict[str, int]:
296
+ self.__post_init__()
297
+ return {
298
+ "portfolio_generation": self.portfolio_generation,
299
+ "recombination_generation": self.recombination_generation,
300
+ }
301
+
302
+
303
+ class ReflectionLaunchMode(str, Enum):
304
+ """When a reflection may enter the agent task queue."""
305
+
306
+ ASYNC_AFTER_STAGE_SEAL = "async_after_stage_seal"
307
+
308
+
309
+ class ReflectionVisibility(str, Enum):
310
+ """Visibility law for evidence produced by a reflection wave."""
311
+
312
+ QUARANTINED_UNTIL_BLOCK_CLOSE = "quarantined_until_block_close"
313
+
314
+
315
+ class ReflectionFailureMode(str, Enum):
316
+ """When a settled reflection failure changes campaign execution."""
317
+
318
+ FAIL_AT_NEXT_STAGE_BOUNDARY = "fail_at_next_stage_boundary"
319
+ COLLECT_ALL_AT_BARRIER_THEN_FAIL = "collect_all_at_barrier_then_fail"
320
+ BEST_EFFORT_DEGRADED = "best_effort_degraded"
321
+
322
+
323
+ @dataclass(frozen=True, slots=True)
324
+ class CampaignReflectionSupervisionPolicy:
325
+ """Failure supervision independent from reflection-content visibility.
326
+
327
+ A visibility barrier controls when successful quarantined content may enter
328
+ later testing. It must not implicitly define when background-task failures
329
+ become observable or how sibling tasks are drained.
330
+ """
331
+
332
+ mode: ReflectionFailureMode = ReflectionFailureMode.COLLECT_ALL_AT_BARRIER_THEN_FAIL
333
+ policy_id: str = field(
334
+ init=False,
335
+ default="campaign_reflection_supervision",
336
+ )
337
+ policy_version: int = field(init=False, default=1)
338
+ definition_sha256: str = field(
339
+ init=False,
340
+ default=hashlib.sha256(
341
+ b"agent-evolve:campaign-reflection-supervision:v1;"
342
+ b"visibility-independent=true;settlement-durable=true;"
343
+ b"safe-fail-checkpoint=after-stage-seal-before-next-stage;"
344
+ b"barrier-drain=all-launched;exception-short-circuit=false;"
345
+ b"failed-content-admission=false;partial-block-admission=false;"
346
+ b"abort-cancellation=typed-and-drained;"
347
+ b"modes=fail-at-next-stage-boundary,collect-all-at-barrier-then-fail,"
348
+ b"best-effort-degraded"
349
+ ).hexdigest(),
350
+ )
351
+
352
+ def __post_init__(self) -> None:
353
+ if type(self.mode) is not ReflectionFailureMode:
354
+ raise TypeError("mode must be an exact ReflectionFailureMode")
355
+ _validate_token(self.policy_id, name="reflection_supervision.policy_id")
356
+ if type(self.policy_version) is not int or self.policy_version != 1:
357
+ raise ValueError("unsupported reflection supervision policy version")
358
+ require_sha256(
359
+ self.definition_sha256,
360
+ "reflection_supervision.definition_sha256",
361
+ )
362
+
363
+ @property
364
+ def configuration_sha256(self) -> str:
365
+ self.__post_init__()
366
+ return _hash(
367
+ _REFLECTION_SUPERVISION_CONFIGURATION_DOMAIN,
368
+ {"mode": self.mode.value},
369
+ )
370
+
371
+ def to_record(self) -> dict[str, object]:
372
+ self.__post_init__()
373
+ return {
374
+ "policy_id": self.policy_id,
375
+ "policy_version": self.policy_version,
376
+ "definition_sha256": self.definition_sha256,
377
+ "mode": self.mode.value,
378
+ "configuration_sha256": self.configuration_sha256,
379
+ "visibility_independent": True,
380
+ "safe_failure_checkpoint": ("after_stage_seal_before_next_archive_cutoff"),
381
+ "barrier_drains_all_launched_reflections": True,
382
+ "failed_or_partial_block_admitted": False,
383
+ }
384
+
385
+
386
+ @dataclass(frozen=True, slots=True)
387
+ class CampaignReflectionWave:
388
+ """Nonblocking reflection work and its prospective visibility barrier."""
389
+
390
+ source_generation: int
391
+ call_count: int
392
+ launch_mode: ReflectionLaunchMode
393
+ visibility: ReflectionVisibility
394
+ promotion_barrier_generation: int | None
395
+
396
+ def __post_init__(self) -> None:
397
+ if type(self.source_generation) is not int or self.source_generation <= 0:
398
+ raise ValueError("reflection source_generation must be positive")
399
+ if type(self.call_count) is not int or self.call_count <= 0:
400
+ raise ValueError("reflection call_count must be positive")
401
+ if type(self.launch_mode) is not ReflectionLaunchMode:
402
+ raise TypeError("launch_mode must be a ReflectionLaunchMode")
403
+ if type(self.visibility) is not ReflectionVisibility:
404
+ raise TypeError("visibility must be a ReflectionVisibility")
405
+ barrier = self.promotion_barrier_generation
406
+ if barrier is not None and (
407
+ type(barrier) is not int or barrier < self.source_generation
408
+ ):
409
+ raise ValueError("promotion barrier cannot precede reflection evidence")
410
+
411
+ def to_record(self) -> dict[str, object]:
412
+ self.__post_init__()
413
+ return {
414
+ "source_generation": self.source_generation,
415
+ "call_count": self.call_count,
416
+ "launch_mode": self.launch_mode.value,
417
+ "visibility": self.visibility.value,
418
+ "promotion_barrier_generation": self.promotion_barrier_generation,
419
+ }
420
+
421
+
422
+ @dataclass(frozen=True, slots=True)
423
+ class CampaignPromotionBarrier:
424
+ """Stage seal after which quarantined reflections may enter testing.
425
+
426
+ The legacy cohort cadence closes a barrier at its last reflection source.
427
+ A delayed cadence may close it at a later sealed stage so provider work can
428
+ overlap intervening generations. In both cases the barrier is prospective:
429
+ admitted evidence can first appear in the *following* stage request.
430
+ """
431
+
432
+ generation: int
433
+ reflection_source_generations: tuple[int, ...]
434
+
435
+ def __post_init__(self) -> None:
436
+ if type(self.generation) is not int or self.generation <= 0:
437
+ raise ValueError("promotion barrier generation must be positive")
438
+ if (
439
+ type(self.reflection_source_generations) is not tuple
440
+ or not self.reflection_source_generations
441
+ or any(
442
+ type(value) is not int or value <= 0
443
+ for value in self.reflection_source_generations
444
+ )
445
+ ):
446
+ raise ValueError("promotion barrier requires exact source generations")
447
+ if self.reflection_source_generations != tuple(
448
+ sorted(set(self.reflection_source_generations))
449
+ ):
450
+ raise ValueError("reflection source generations must be canonical")
451
+ if self.reflection_source_generations[-1] > self.generation:
452
+ raise ValueError("promotion barrier cannot precede its last source")
453
+
454
+ def to_record(self) -> dict[str, object]:
455
+ self.__post_init__()
456
+ return {
457
+ "generation": self.generation,
458
+ "reflection_source_generations": list(self.reflection_source_generations),
459
+ }
460
+
461
+
462
+ @dataclass(frozen=True, slots=True)
463
+ class CampaignSchedule:
464
+ """Authenticated operator chronology derived from a campaign protocol."""
465
+
466
+ protocol_sha256: str
467
+ cadence_policy_id: str
468
+ cadence_policy_version: int
469
+ cadence_definition_sha256: str
470
+ steps: tuple[CampaignGenerationStep, ...]
471
+ recombination_pairs: tuple[CampaignGenerationPair, ...]
472
+ reflection_waves: tuple[CampaignReflectionWave, ...]
473
+ promotion_barriers: tuple[CampaignPromotionBarrier, ...]
474
+
475
+ def __post_init__(self) -> None:
476
+ require_sha256(self.protocol_sha256, "protocol_sha256")
477
+ _validate_token(self.cadence_policy_id, name="cadence_policy_id")
478
+ if (
479
+ type(self.cadence_policy_version) is not int
480
+ or self.cadence_policy_version <= 0
481
+ ):
482
+ raise ValueError("cadence_policy_version must be positive")
483
+ require_sha256(
484
+ self.cadence_definition_sha256,
485
+ "cadence_definition_sha256",
486
+ )
487
+ if type(self.steps) is not tuple or not self.steps:
488
+ raise ValueError("steps must be a non-empty exact tuple")
489
+ if any(type(step) is not CampaignGenerationStep for step in self.steps):
490
+ raise TypeError("steps must contain exact CampaignGenerationStep values")
491
+ for step in self.steps:
492
+ CampaignGenerationStep.__post_init__(step)
493
+ if tuple(step.generation for step in self.steps) != tuple(
494
+ range(1, len(self.steps) + 1)
495
+ ):
496
+ raise ValueError("schedule generations must be contiguous from one")
497
+ expected_kinds = tuple(
498
+ CampaignGenerationKind.PORTFOLIO
499
+ if index % 2 == 1
500
+ else CampaignGenerationKind.RECOMBINATION
501
+ for index in range(1, len(self.steps) + 1)
502
+ )
503
+ if tuple(step.kind for step in self.steps) != expected_kinds:
504
+ raise ValueError("schedule must alternate portfolio then recombination")
505
+ expected_pairs = tuple(
506
+ CampaignGenerationPair(index, index + 1)
507
+ for index in range(1, len(self.steps), 2)
508
+ )
509
+ if self.recombination_pairs != expected_pairs:
510
+ raise ValueError("recombination_pairs differ from the exact chronology")
511
+ for pair in self.recombination_pairs:
512
+ recombination = self.steps[pair.recombination_generation - 1]
513
+ if recombination.source_portfolio_generation != pair.portfolio_generation:
514
+ raise ValueError(
515
+ "recombination source differs from its paired portfolio"
516
+ )
517
+ if type(self.reflection_waves) is not tuple or any(
518
+ type(value) is not CampaignReflectionWave for value in self.reflection_waves
519
+ ):
520
+ raise TypeError("reflection_waves must contain exact wave values")
521
+ for value in self.reflection_waves:
522
+ CampaignReflectionWave.__post_init__(value)
523
+ reflection_sources = tuple(
524
+ value.source_generation for value in self.reflection_waves
525
+ )
526
+ if reflection_sources != tuple(sorted(set(reflection_sources))):
527
+ raise ValueError("reflection waves must have unique canonical sources")
528
+ recombination_calls = {
529
+ step.generation: step.planned_agent_calls
530
+ for step in self.steps
531
+ if step.kind is CampaignGenerationKind.RECOMBINATION
532
+ and step.planned_agent_calls > 0
533
+ }
534
+ if reflection_sources != tuple(recombination_calls):
535
+ raise ValueError("reflection waves differ from recombination call plans")
536
+ if any(
537
+ value.call_count != recombination_calls[value.source_generation]
538
+ for value in self.reflection_waves
539
+ ):
540
+ raise ValueError("reflection wave call count differs from its stage")
541
+ if type(self.promotion_barriers) is not tuple or any(
542
+ type(value) is not CampaignPromotionBarrier
543
+ for value in self.promotion_barriers
544
+ ):
545
+ raise TypeError("promotion_barriers must contain exact barrier values")
546
+ for value in self.promotion_barriers:
547
+ CampaignPromotionBarrier.__post_init__(value)
548
+ if value.generation > len(self.steps):
549
+ raise ValueError("promotion barrier escapes the campaign horizon")
550
+ barrier_step = self.steps[value.generation - 1]
551
+ if barrier_step.kind is not CampaignGenerationKind.RECOMBINATION:
552
+ raise ValueError("promotion barrier must follow a recombination seal")
553
+ barrier_generations = tuple(
554
+ value.generation for value in self.promotion_barriers
555
+ )
556
+ if barrier_generations != tuple(sorted(set(barrier_generations))):
557
+ raise ValueError("promotion barriers must be unique and canonical")
558
+ waves_by_barrier: dict[int, list[int]] = {}
559
+ for wave in self.reflection_waves:
560
+ barrier = wave.promotion_barrier_generation
561
+ if barrier is not None:
562
+ waves_by_barrier.setdefault(barrier, []).append(wave.source_generation)
563
+ expected_barriers = tuple(
564
+ CampaignPromotionBarrier(generation, tuple(sources))
565
+ for generation, sources in sorted(waves_by_barrier.items())
566
+ )
567
+ if expected_barriers != self.promotion_barriers:
568
+ raise ValueError("promotion barriers differ from reflection visibility")
569
+
570
+ @property
571
+ def portfolio_generations(self) -> tuple[int, ...]:
572
+ return tuple(
573
+ step.generation
574
+ for step in self.steps
575
+ if step.kind is CampaignGenerationKind.PORTFOLIO
576
+ )
577
+
578
+ @property
579
+ def paired_recombination_generations(self) -> tuple[int, ...]:
580
+ return tuple(pair.recombination_generation for pair in self.recombination_pairs)
581
+
582
+ @property
583
+ def unpaired_terminal_portfolio_generation(self) -> int | None:
584
+ terminal = self.steps[-1]
585
+ return (
586
+ terminal.generation
587
+ if terminal.kind is CampaignGenerationKind.PORTFOLIO
588
+ else None
589
+ )
590
+
591
+ @property
592
+ def planned_candidate_evaluations(self) -> int:
593
+ return sum(step.planned_candidate_evaluations for step in self.steps)
594
+
595
+ @property
596
+ def planned_agent_calls(self) -> int:
597
+ return sum(step.planned_agent_calls for step in self.steps)
598
+
599
+ def to_record(self) -> dict[str, object]:
600
+ self.__post_init__()
601
+ return {
602
+ "schema_version": 1,
603
+ "protocol_sha256": self.protocol_sha256,
604
+ "cadence": {
605
+ "policy_id": self.cadence_policy_id,
606
+ "policy_version": self.cadence_policy_version,
607
+ "definition_sha256": self.cadence_definition_sha256,
608
+ },
609
+ "steps": [step.to_record() for step in self.steps],
610
+ "portfolio_generations": list(self.portfolio_generations),
611
+ "recombination_pairs": [
612
+ pair.to_record() for pair in self.recombination_pairs
613
+ ],
614
+ "paired_recombination_generations": list(
615
+ self.paired_recombination_generations
616
+ ),
617
+ "reflection_waves": [value.to_record() for value in self.reflection_waves],
618
+ "promotion_barriers": [
619
+ value.to_record() for value in self.promotion_barriers
620
+ ],
621
+ "unpaired_terminal_portfolio_generation": (
622
+ self.unpaired_terminal_portfolio_generation
623
+ ),
624
+ "planned_candidate_evaluations": self.planned_candidate_evaluations,
625
+ "planned_agent_calls": self.planned_agent_calls,
626
+ }
627
+
628
+ @property
629
+ def schedule_sha256(self) -> str:
630
+ return _hash(_SCHEDULE_DOMAIN, self.to_record())
631
+
632
+
633
+ @runtime_checkable
634
+ class CampaignCadence(Protocol):
635
+ policy_id: str
636
+ policy_version: int
637
+ definition_sha256: str
638
+
639
+ def build(self, protocol: CampaignProtocol) -> CampaignSchedule: ...
640
+
641
+
642
+ def _build_alternating_schedule(
643
+ protocol: CampaignProtocol,
644
+ *,
645
+ cadence_policy_id: str,
646
+ cadence_policy_version: int,
647
+ cadence_definition_sha256: str,
648
+ reflection_waves: tuple[CampaignReflectionWave, ...],
649
+ promotion_barriers: tuple[CampaignPromotionBarrier, ...],
650
+ ) -> CampaignSchedule:
651
+ """Materialize common P/R stage accounting around a reflection policy."""
652
+
653
+ reflection_generation_set = {
654
+ value.source_generation for value in reflection_waves
655
+ }
656
+ steps = tuple(
657
+ CampaignGenerationStep(
658
+ generation=generation,
659
+ kind=(
660
+ CampaignGenerationKind.PORTFOLIO
661
+ if generation % 2 == 1
662
+ else CampaignGenerationKind.RECOMBINATION
663
+ ),
664
+ source_portfolio_generation=(
665
+ None if generation % 2 == 1 else generation - 1
666
+ ),
667
+ parent_count=protocol.parents_per_portfolio_generation,
668
+ offspring_per_parent=(
669
+ protocol.portfolio_width
670
+ if generation % 2 == 1
671
+ else protocol.recombinations_per_parent
672
+ ),
673
+ planned_agent_calls=(
674
+ protocol.parents_per_portfolio_generation
675
+ if generation % 2 == 1
676
+ else (
677
+ protocol.reflections_per_recombination_generation
678
+ if generation in reflection_generation_set
679
+ else 0
680
+ )
681
+ ),
682
+ )
683
+ for generation in range(1, protocol.generation_count + 1)
684
+ )
685
+ pairs = tuple(
686
+ CampaignGenerationPair(generation, generation + 1)
687
+ for generation in range(1, protocol.generation_count, 2)
688
+ )
689
+ return CampaignSchedule(
690
+ protocol_sha256=protocol.protocol_sha256,
691
+ cadence_policy_id=cadence_policy_id,
692
+ cadence_policy_version=cadence_policy_version,
693
+ cadence_definition_sha256=cadence_definition_sha256,
694
+ steps=steps,
695
+ recombination_pairs=pairs,
696
+ reflection_waves=reflection_waves,
697
+ promotion_barriers=promotion_barriers,
698
+ )
699
+
700
+
701
+ @dataclass(frozen=True, slots=True)
702
+ class AlternatingPortfolioRecombinationCadence:
703
+ """Portfolio-first cadence for three through twenty-four generations."""
704
+
705
+ policy_id: str = field(init=False, default=ALTERNATING_CADENCE_POLICY_ID)
706
+ policy_version: int = field(
707
+ init=False,
708
+ default=ALTERNATING_CADENCE_POLICY_VERSION,
709
+ )
710
+ definition_sha256: str = field(
711
+ init=False,
712
+ default=ALTERNATING_CADENCE_DEFINITION_SHA256,
713
+ )
714
+
715
+ def __post_init__(self) -> None:
716
+ if (
717
+ self.policy_id != ALTERNATING_CADENCE_POLICY_ID
718
+ or self.policy_version != ALTERNATING_CADENCE_POLICY_VERSION
719
+ or self.definition_sha256 != ALTERNATING_CADENCE_DEFINITION_SHA256
720
+ ):
721
+ raise ValueError("alternating cadence policy identity changed")
722
+
723
+ def build(self, protocol: CampaignProtocol) -> CampaignSchedule:
724
+ if type(protocol) is not CampaignProtocol:
725
+ raise TypeError("protocol must be an exact CampaignProtocol")
726
+ CampaignProtocol.__post_init__(protocol)
727
+ paired_recombination_generations = tuple(
728
+ range(2, protocol.generation_count + 1, 2)
729
+ )
730
+ candidate_reflection_generations = tuple(
731
+ generation
732
+ for generation in paired_recombination_generations
733
+ if (
734
+ protocol.reflections_per_recombination_generation > 0
735
+ and (
736
+ protocol.terminal_reflection_policy
737
+ is TerminalReflectionPolicy.ALLOW_TERMINAL
738
+ or generation < protocol.generation_count
739
+ )
740
+ )
741
+ )
742
+ block_size = protocol.reflection_promotion_block_pairs
743
+ barrier_by_source: dict[int, int] = {}
744
+ promotion_barriers: list[CampaignPromotionBarrier] = []
745
+ if protocol.reflections_per_recombination_generation > 0:
746
+ for start in range(
747
+ 0,
748
+ len(candidate_reflection_generations),
749
+ block_size,
750
+ ):
751
+ block = candidate_reflection_generations[start : start + block_size]
752
+ if len(block) != block_size:
753
+ continue
754
+ barrier = block[-1]
755
+ promotion_barriers.append(
756
+ CampaignPromotionBarrier(
757
+ generation=barrier,
758
+ reflection_source_generations=block,
759
+ )
760
+ )
761
+ barrier_by_source.update({source: barrier for source in block})
762
+ reflection_generations = candidate_reflection_generations
763
+ if (
764
+ protocol.terminal_reflection_policy
765
+ is TerminalReflectionPolicy.REQUIRE_FUTURE_PORTFOLIO_CONSUMER
766
+ ):
767
+ # Merely occurring before a later portfolio is insufficient. If a
768
+ # cohort is incomplete, no reflection in it can cross a promotion
769
+ # barrier, so none has an actual consumer under this policy.
770
+ reflection_generations = tuple(
771
+ generation
772
+ for generation in reflection_generations
773
+ if generation in barrier_by_source
774
+ and barrier_by_source[generation] + 1
775
+ <= protocol.generation_count
776
+ )
777
+ retained = set(reflection_generations)
778
+ promotion_barriers = [
779
+ barrier
780
+ for barrier in promotion_barriers
781
+ if set(barrier.reflection_source_generations) <= retained
782
+ ]
783
+ reflection_waves = tuple(
784
+ CampaignReflectionWave(
785
+ source_generation=generation,
786
+ call_count=protocol.reflections_per_recombination_generation,
787
+ launch_mode=ReflectionLaunchMode.ASYNC_AFTER_STAGE_SEAL,
788
+ visibility=ReflectionVisibility.QUARANTINED_UNTIL_BLOCK_CLOSE,
789
+ promotion_barrier_generation=barrier_by_source.get(generation),
790
+ )
791
+ for generation in reflection_generations
792
+ )
793
+ return _build_alternating_schedule(
794
+ protocol,
795
+ cadence_policy_id=self.policy_id,
796
+ cadence_policy_version=self.policy_version,
797
+ cadence_definition_sha256=self.definition_sha256,
798
+ reflection_waves=reflection_waves,
799
+ promotion_barriers=tuple(promotion_barriers),
800
+ )
801
+
802
+
803
+ @dataclass(frozen=True, slots=True)
804
+ class SealedCutoffDelayedAdmissionCadence:
805
+ """P/R cadence with one-pair-lagged, prospectively useful reflection.
806
+
807
+ A reflection sourced from sealed recombination generation ``g`` is allowed
808
+ to run while generations ``g+1`` and ``g+2`` execute. Its join barrier is
809
+ the seal of recombination generation ``g+2`` and its first possible
810
+ consumer is portfolio generation ``g+3``. Sources without that exact
811
+ future consumer are never launched.
812
+
813
+ This is a separate injected cadence rather than a flag on the legacy
814
+ cadence, preserving the existing default schedule and policy identity.
815
+ """
816
+
817
+ policy_id: str = field(
818
+ init=False,
819
+ default=SEALED_CUTOFF_DELAYED_CADENCE_POLICY_ID,
820
+ )
821
+ policy_version: int = field(
822
+ init=False,
823
+ default=SEALED_CUTOFF_DELAYED_CADENCE_POLICY_VERSION,
824
+ )
825
+ definition_sha256: str = field(
826
+ init=False,
827
+ default=SEALED_CUTOFF_DELAYED_CADENCE_DEFINITION_SHA256,
828
+ )
829
+
830
+ def __post_init__(self) -> None:
831
+ if (
832
+ self.policy_id != SEALED_CUTOFF_DELAYED_CADENCE_POLICY_ID
833
+ or self.policy_version != SEALED_CUTOFF_DELAYED_CADENCE_POLICY_VERSION
834
+ or self.definition_sha256
835
+ != SEALED_CUTOFF_DELAYED_CADENCE_DEFINITION_SHA256
836
+ ):
837
+ raise ValueError("sealed-cutoff delayed cadence identity changed")
838
+
839
+ def build(self, protocol: CampaignProtocol) -> CampaignSchedule:
840
+ if type(protocol) is not CampaignProtocol:
841
+ raise TypeError("protocol must be an exact CampaignProtocol")
842
+ CampaignProtocol.__post_init__(protocol)
843
+ if protocol.terminal_reflection_policy is not (
844
+ TerminalReflectionPolicy.REQUIRE_FUTURE_PORTFOLIO_CONSUMER
845
+ ):
846
+ raise ValueError(
847
+ "delayed admission requires the future-consumer terminal policy"
848
+ )
849
+ if protocol.reflection_promotion_block_pairs != 1:
850
+ raise ValueError(
851
+ "delayed admission requires one reflection per admission barrier"
852
+ )
853
+
854
+ sources = tuple(
855
+ generation
856
+ for generation in range(2, protocol.generation_count + 1, 2)
857
+ if (
858
+ protocol.reflections_per_recombination_generation > 0
859
+ and generation + 3 <= protocol.generation_count
860
+ )
861
+ )
862
+ barriers = tuple(
863
+ CampaignPromotionBarrier(
864
+ generation=source + 2,
865
+ reflection_source_generations=(source,),
866
+ )
867
+ for source in sources
868
+ )
869
+ waves = tuple(
870
+ CampaignReflectionWave(
871
+ source_generation=source,
872
+ call_count=protocol.reflections_per_recombination_generation,
873
+ launch_mode=ReflectionLaunchMode.ASYNC_AFTER_STAGE_SEAL,
874
+ visibility=ReflectionVisibility.QUARANTINED_UNTIL_BLOCK_CLOSE,
875
+ promotion_barrier_generation=source + 2,
876
+ )
877
+ for source in sources
878
+ )
879
+ return _build_alternating_schedule(
880
+ protocol,
881
+ cadence_policy_id=self.policy_id,
882
+ cadence_policy_version=self.policy_version,
883
+ cadence_definition_sha256=self.definition_sha256,
884
+ reflection_waves=waves,
885
+ promotion_barriers=barriers,
886
+ )
887
+
888
+
889
+ @dataclass(frozen=True, slots=True)
890
+ class CampaignConcurrency:
891
+ """Independent evaluator and agent-queue concurrency caps."""
892
+
893
+ evaluator_concurrency: int
894
+ agent_concurrency: int
895
+ agent_queue_capacity: int
896
+
897
+ def __post_init__(self) -> None:
898
+ for name in ("evaluator_concurrency", "agent_concurrency"):
899
+ value = getattr(self, name)
900
+ if type(value) is not int or value <= 0:
901
+ raise ValueError(f"{name} must be a positive exact integer")
902
+ if (
903
+ type(self.agent_queue_capacity) is not int
904
+ or self.agent_queue_capacity < self.agent_concurrency
905
+ ):
906
+ raise ValueError("agent_queue_capacity must be at least agent_concurrency")
907
+
908
+ def to_record(self) -> dict[str, int]:
909
+ self.__post_init__()
910
+ return {
911
+ "evaluator_concurrency": self.evaluator_concurrency,
912
+ "agent_concurrency": self.agent_concurrency,
913
+ "agent_queue_capacity": self.agent_queue_capacity,
914
+ }
915
+
916
+ @property
917
+ def concurrency_sha256(self) -> str:
918
+ return _hash(_CONCURRENCY_DOMAIN, self.to_record())
919
+
920
+
921
+ @dataclass(frozen=True, slots=True)
922
+ class BenchmarkSessionRequest:
923
+ """Workload-neutral request passed to a benchmark session port."""
924
+
925
+ protocol_sha256: str
926
+ budget_sha256: str
927
+ outer_seed: int
928
+ requested_evaluator_concurrency: int
929
+
930
+ def __post_init__(self) -> None:
931
+ require_sha256(self.protocol_sha256, "protocol_sha256")
932
+ require_sha256(self.budget_sha256, "budget_sha256")
933
+ if type(self.outer_seed) is not int or not -(1 << 127) <= self.outer_seed < (
934
+ 1 << 127
935
+ ):
936
+ raise ValueError("outer_seed must be an exact signed int128")
937
+ if (
938
+ type(self.requested_evaluator_concurrency) is not int
939
+ or self.requested_evaluator_concurrency <= 0
940
+ ):
941
+ raise ValueError("requested evaluator concurrency must be positive")
942
+
943
+ def to_record(self) -> dict[str, object]:
944
+ self.__post_init__()
945
+ return {
946
+ "protocol_sha256": self.protocol_sha256,
947
+ "budget_sha256": self.budget_sha256,
948
+ "outer_seed": self.outer_seed,
949
+ "requested_evaluator_concurrency": (self.requested_evaluator_concurrency),
950
+ }
951
+
952
+ @property
953
+ def request_sha256(self) -> str:
954
+ return _hash(_SESSION_REQUEST_DOMAIN, self.to_record())
955
+
956
+
957
+ @dataclass(frozen=True, slots=True)
958
+ class CampaignBenchmarkSession:
959
+ """Benchmark facts and acquired-resource evidence from preflight."""
960
+
961
+ request_sha256: str
962
+ benchmark: FrozenJsonObject
963
+ evaluator_concurrency_cap: int
964
+ preflight_receipt: FrozenJsonObject
965
+ resource_lease: FrozenJsonObject
966
+
967
+ def __post_init__(self) -> None:
968
+ require_sha256(self.request_sha256, "request_sha256")
969
+ _validate_frozen_object(self.benchmark, name="benchmark")
970
+ _validate_frozen_object(self.preflight_receipt, name="preflight_receipt")
971
+ _validate_frozen_object(self.resource_lease, name="resource_lease")
972
+ if (
973
+ type(self.evaluator_concurrency_cap) is not int
974
+ or self.evaluator_concurrency_cap <= 0
975
+ ):
976
+ raise ValueError("evaluator_concurrency_cap must be positive")
977
+
978
+ def to_record(self) -> dict[str, object]:
979
+ self.__post_init__()
980
+ return {
981
+ "request_sha256": self.request_sha256,
982
+ "benchmark": thaw_json(self.benchmark),
983
+ "benchmark_sha256": typed_json_sha256(self.benchmark),
984
+ "evaluator_concurrency_cap": self.evaluator_concurrency_cap,
985
+ "preflight_receipt": thaw_json(self.preflight_receipt),
986
+ "resource_lease": thaw_json(self.resource_lease),
987
+ }
988
+
989
+ @property
990
+ def session_sha256(self) -> str:
991
+ return _hash(_SESSION_DOMAIN, self.to_record())
992
+
993
+
994
+ @dataclass(frozen=True, slots=True)
995
+ class CampaignSeed:
996
+ """One labelled, immutable benchmark seed configuration."""
997
+
998
+ seed_id: str
999
+ configuration: FrozenJsonObject
1000
+
1001
+ def __post_init__(self) -> None:
1002
+ _validate_token(self.seed_id, name="seed_id")
1003
+ _validate_frozen_object(self.configuration, name="configuration")
1004
+
1005
+ @property
1006
+ def configuration_sha256(self) -> str:
1007
+ return typed_json_sha256(self.configuration)
1008
+
1009
+ def to_record(self) -> dict[str, object]:
1010
+ self.__post_init__()
1011
+ record = {
1012
+ "seed_id": self.seed_id,
1013
+ "configuration_sha256": self.configuration_sha256,
1014
+ }
1015
+ return {**record, "seed_sha256": _hash(_SEED_DOMAIN, record)}
1016
+
1017
+
1018
+ @dataclass(frozen=True, slots=True)
1019
+ class CampaignSeedBatch:
1020
+ """Exact seed batch bound to one opened benchmark session."""
1021
+
1022
+ session_sha256: str
1023
+ seeds: tuple[CampaignSeed, ...]
1024
+
1025
+ def __post_init__(self) -> None:
1026
+ require_sha256(self.session_sha256, "session_sha256")
1027
+ if type(self.seeds) is not tuple or not self.seeds:
1028
+ raise ValueError("seeds must be a non-empty exact tuple")
1029
+ if any(type(seed) is not CampaignSeed for seed in self.seeds):
1030
+ raise TypeError("seeds must contain exact CampaignSeed values")
1031
+ for seed in self.seeds:
1032
+ CampaignSeed.__post_init__(seed)
1033
+ if len({seed.seed_id for seed in self.seeds}) != len(self.seeds):
1034
+ raise ValueError("seed IDs must be unique")
1035
+ if len({seed.configuration_sha256 for seed in self.seeds}) != len(self.seeds):
1036
+ raise ValueError("seed configurations must be unique")
1037
+
1038
+ def to_record(self) -> dict[str, object]:
1039
+ self.__post_init__()
1040
+ return {
1041
+ "session_sha256": self.session_sha256,
1042
+ "seeds": [seed.to_record() for seed in self.seeds],
1043
+ }
1044
+
1045
+ @property
1046
+ def batch_sha256(self) -> str:
1047
+ return _hash(_SEED_BATCH_DOMAIN, self.to_record())
1048
+
1049
+
1050
+ @dataclass(frozen=True, slots=True)
1051
+ class ParentVariationBinding:
1052
+ """Catalog result sealed to one benchmark, parent, and novelty cutoff."""
1053
+
1054
+ benchmark_sha256: str
1055
+ parent_configuration_sha256: str
1056
+ known_phenotype_sha256s: tuple[str, ...]
1057
+ contract: FiniteVariationContract
1058
+ eligibility_receipt: FiniteVariationEligibilityReceipt | None = None
1059
+
1060
+ def __post_init__(self) -> None:
1061
+ require_sha256(self.benchmark_sha256, "benchmark_sha256")
1062
+ require_sha256(
1063
+ self.parent_configuration_sha256,
1064
+ "parent_configuration_sha256",
1065
+ )
1066
+ if type(self.known_phenotype_sha256s) is not tuple or any(
1067
+ type(value) is not str for value in self.known_phenotype_sha256s
1068
+ ):
1069
+ raise TypeError("known_phenotype_sha256s must be an exact string tuple")
1070
+ for value in self.known_phenotype_sha256s:
1071
+ require_sha256(value, "known_phenotype_sha256")
1072
+ if self.known_phenotype_sha256s != tuple(
1073
+ sorted(set(self.known_phenotype_sha256s))
1074
+ ):
1075
+ raise ValueError("known_phenotype_sha256s must be unique and canonical")
1076
+ if type(self.contract) is not FiniteVariationContract:
1077
+ raise TypeError("contract must be an exact FiniteVariationContract")
1078
+ validate_finite_variation_contract(self.contract)
1079
+ if (
1080
+ typed_json_sha256(self.contract.parent_configuration)
1081
+ != self.parent_configuration_sha256
1082
+ ):
1083
+ raise ValueError("finite contract is bound to a different parent")
1084
+ if self.eligibility_receipt is not None:
1085
+ if type(self.eligibility_receipt) is not FiniteVariationEligibilityReceipt:
1086
+ raise TypeError("eligibility_receipt must be exact or None")
1087
+ FiniteVariationEligibilityReceipt.__post_init__(self.eligibility_receipt)
1088
+ if (
1089
+ self.eligibility_receipt.eligible_contract_identity_sha256
1090
+ != self.contract.identity_sha256
1091
+ ):
1092
+ raise ValueError("finite contract differs from its eligibility receipt")
1093
+ if (
1094
+ self.eligibility_receipt.known_phenotype_sha256s
1095
+ != self.known_phenotype_sha256s
1096
+ ):
1097
+ raise ValueError("eligibility receipt differs from the novelty cutoff")
1098
+
1099
+ def to_record(self) -> dict[str, object]:
1100
+ self.__post_init__()
1101
+ return {
1102
+ "benchmark_sha256": self.benchmark_sha256,
1103
+ "parent_configuration_sha256": self.parent_configuration_sha256,
1104
+ "known_phenotype_sha256s": list(self.known_phenotype_sha256s),
1105
+ "finite_variation_contract_sha256": self.contract.identity_sha256,
1106
+ "eligibility_receipt": (
1107
+ None
1108
+ if self.eligibility_receipt is None
1109
+ else self.eligibility_receipt.to_record()
1110
+ ),
1111
+ }
1112
+
1113
+
1114
+ def _port_record(port: object, *, role: str) -> dict[str, object]:
1115
+ for name in ("port_id", "port_version", "definition_sha256"):
1116
+ if not hasattr(port, name):
1117
+ raise TypeError(f"{role} port is missing {name}")
1118
+ port_id = getattr(port, "port_id")
1119
+ port_version = getattr(port, "port_version")
1120
+ definition_sha256 = getattr(port, "definition_sha256")
1121
+ _validate_token(port_id, name=f"{role}.port_id")
1122
+ if type(port_version) is not int or port_version <= 0:
1123
+ raise ValueError(f"{role}.port_version must be positive")
1124
+ require_sha256(definition_sha256, f"{role}.definition_sha256")
1125
+ return {
1126
+ "role": role,
1127
+ "port_id": port_id,
1128
+ "port_version": port_version,
1129
+ "definition_sha256": definition_sha256,
1130
+ }
1131
+
1132
+
1133
+ @runtime_checkable
1134
+ class CampaignBenchmarkPort(Protocol):
1135
+ port_id: str
1136
+ port_version: int
1137
+ definition_sha256: str
1138
+
1139
+ def open(self, request: BenchmarkSessionRequest) -> CampaignBenchmarkSession: ...
1140
+
1141
+
1142
+ @runtime_checkable
1143
+ class CampaignSeedPort(Protocol):
1144
+ port_id: str
1145
+ port_version: int
1146
+ definition_sha256: str
1147
+
1148
+ def load(self, session: CampaignBenchmarkSession) -> CampaignSeedBatch: ...
1149
+
1150
+
1151
+ @runtime_checkable
1152
+ class CampaignCatalogPort(Protocol):
1153
+ port_id: str
1154
+ port_version: int
1155
+ definition_sha256: str
1156
+
1157
+ def bind(
1158
+ self,
1159
+ benchmark: FrozenJsonObject,
1160
+ parent: FrozenJsonObject,
1161
+ known_phenotype_sha256s: tuple[str, ...],
1162
+ ) -> ParentVariationBinding: ...
1163
+
1164
+
1165
+ @runtime_checkable
1166
+ class CampaignEvidencePort(Protocol):
1167
+ port_id: str
1168
+ port_version: int
1169
+ definition_sha256: str
1170
+
1171
+ def initialize_memory(
1172
+ self,
1173
+ session: CampaignBenchmarkSession,
1174
+ seeds: CampaignSeedBatch,
1175
+ ) -> FrozenJsonObject: ...
1176
+
1177
+ def context(
1178
+ self,
1179
+ session: CampaignBenchmarkSession,
1180
+ parent: FrozenJsonObject,
1181
+ variation: ParentVariationBinding,
1182
+ memory: FrozenJsonObject,
1183
+ ) -> FrozenJsonObject: ...
1184
+
1185
+ def cards(
1186
+ self,
1187
+ session: CampaignBenchmarkSession,
1188
+ parent: FrozenJsonObject,
1189
+ variation: ParentVariationBinding,
1190
+ memory: FrozenJsonObject,
1191
+ ) -> tuple[FrozenJsonObject, ...]: ...
1192
+
1193
+
1194
+ @dataclass(frozen=True, slots=True)
1195
+ class CampaignWorkloadPorts:
1196
+ """The complete benchmark-owned side of the inversion boundary."""
1197
+
1198
+ benchmark: CampaignBenchmarkPort
1199
+ seeds: CampaignSeedPort
1200
+ catalog: CampaignCatalogPort
1201
+ evidence: CampaignEvidencePort
1202
+
1203
+ def __post_init__(self) -> None:
1204
+ expected = (
1205
+ ("benchmark", self.benchmark, CampaignBenchmarkPort),
1206
+ ("seeds", self.seeds, CampaignSeedPort),
1207
+ ("catalog", self.catalog, CampaignCatalogPort),
1208
+ ("evidence", self.evidence, CampaignEvidencePort),
1209
+ )
1210
+ for role, value, contract in expected:
1211
+ if not isinstance(value, contract):
1212
+ raise TypeError(f"{role} must implement its campaign port")
1213
+ _port_record(value, role=role)
1214
+
1215
+ def to_record(self) -> dict[str, object]:
1216
+ self.__post_init__()
1217
+ return {
1218
+ "benchmark": _port_record(self.benchmark, role="benchmark"),
1219
+ "seeds": _port_record(self.seeds, role="seeds"),
1220
+ "catalog": _port_record(self.catalog, role="catalog"),
1221
+ "evidence": _port_record(self.evidence, role="evidence"),
1222
+ }
1223
+
1224
+ @property
1225
+ def ports_sha256(self) -> str:
1226
+ return _hash(_WORKLOAD_PORTS_DOMAIN, self.to_record())
1227
+
1228
+
1229
+ @dataclass(frozen=True, slots=True)
1230
+ class CampaignPolicyBinding:
1231
+ """An executable policy dependency paired with its public identity."""
1232
+
1233
+ implementation: object = field(repr=False, compare=False)
1234
+ policy_id: str
1235
+ policy_version: int
1236
+ definition_sha256: str
1237
+
1238
+ def __post_init__(self) -> None:
1239
+ if self.implementation is None:
1240
+ raise TypeError("policy implementation cannot be None")
1241
+ _validate_token(self.policy_id, name="policy_id")
1242
+ if type(self.policy_version) is not int or self.policy_version <= 0:
1243
+ raise ValueError("policy_version must be positive")
1244
+ require_sha256(self.definition_sha256, "definition_sha256")
1245
+
1246
+ def to_record(self) -> dict[str, object]:
1247
+ self.__post_init__()
1248
+ return {
1249
+ "policy_id": self.policy_id,
1250
+ "policy_version": self.policy_version,
1251
+ "definition_sha256": self.definition_sha256,
1252
+ }
1253
+
1254
+
1255
+ @dataclass(frozen=True, slots=True)
1256
+ class ArchiveUtilitySnapshot:
1257
+ """Frozen pre-generation utility/reference receipt for generic credit.
1258
+
1259
+ ``snapshot_receipt`` is workload-owned data. It may encode a fixed
1260
+ hypervolume reference, normalization constants, or another archive utility
1261
+ definition, while this core sees only immutable typed JSON and identities.
1262
+ """
1263
+
1264
+ utility_id: str
1265
+ utility_version: int
1266
+ definition_sha256: str
1267
+ generation: int
1268
+ benchmark_sha256: str
1269
+ archive_sha256: str
1270
+ snapshot_receipt: FrozenJsonObject
1271
+ scalar_utility_hex: str | None = None
1272
+
1273
+ def __post_init__(self) -> None:
1274
+ _validate_token(self.utility_id, name="utility_id")
1275
+ if type(self.utility_version) is not int or self.utility_version <= 0:
1276
+ raise ValueError("utility_version must be positive")
1277
+ require_sha256(self.definition_sha256, "definition_sha256")
1278
+ if type(self.generation) is not int or self.generation <= 0:
1279
+ raise ValueError("utility snapshot generation must be positive")
1280
+ require_sha256(self.benchmark_sha256, "benchmark_sha256")
1281
+ require_sha256(self.archive_sha256, "archive_sha256")
1282
+ _validate_frozen_object(self.snapshot_receipt, name="snapshot_receipt")
1283
+ if self.scalar_utility_hex is not None:
1284
+ if type(self.scalar_utility_hex) is not str:
1285
+ raise TypeError("scalar_utility_hex must be an exact string or None")
1286
+ try:
1287
+ scalar = float.fromhex(self.scalar_utility_hex)
1288
+ except ValueError as error:
1289
+ raise ValueError("scalar_utility_hex must be a float hex string") from error
1290
+ if not math.isfinite(scalar) or scalar < 0.0:
1291
+ raise ValueError(
1292
+ "scalar_utility_hex must encode a finite non-negative utility"
1293
+ )
1294
+ if scalar.hex() != self.scalar_utility_hex:
1295
+ raise ValueError("scalar_utility_hex must be canonical")
1296
+
1297
+ def _unsigned_record(self) -> dict[str, object]:
1298
+ self.__post_init__()
1299
+ record = {
1300
+ "utility_id": self.utility_id,
1301
+ "utility_version": self.utility_version,
1302
+ "definition_sha256": self.definition_sha256,
1303
+ "generation": self.generation,
1304
+ "benchmark_sha256": self.benchmark_sha256,
1305
+ "archive_sha256": self.archive_sha256,
1306
+ "snapshot_receipt": thaw_json(self.snapshot_receipt),
1307
+ }
1308
+ if self.scalar_utility_hex is not None:
1309
+ record["scalar_utility_hex"] = self.scalar_utility_hex
1310
+ return record
1311
+
1312
+ @property
1313
+ def snapshot_sha256(self) -> str:
1314
+ return _hash(_ARCHIVE_UTILITY_SNAPSHOT_DOMAIN, self._unsigned_record())
1315
+
1316
+ def to_record(self) -> dict[str, object]:
1317
+ return {**self._unsigned_record(), "snapshot_sha256": self.snapshot_sha256}
1318
+
1319
+
1320
+ @runtime_checkable
1321
+ class ArchiveUtilityPort(Protocol):
1322
+ """Freeze one workload-defined archive utility before a generation."""
1323
+
1324
+ utility_id: str
1325
+ utility_version: int
1326
+ definition_sha256: str
1327
+
1328
+ def freeze(
1329
+ self,
1330
+ *,
1331
+ benchmark: FrozenJsonObject,
1332
+ generation: int,
1333
+ archive: FrozenJsonObject,
1334
+ ) -> ArchiveUtilitySnapshot: ...
1335
+
1336
+
1337
+ def freeze_archive_utility(
1338
+ port: ArchiveUtilityPort,
1339
+ *,
1340
+ benchmark: FrozenJsonObject,
1341
+ generation: int,
1342
+ archive: FrozenJsonObject,
1343
+ ) -> ArchiveUtilitySnapshot:
1344
+ """Call and authenticate an injected pre-generation utility snapshot."""
1345
+
1346
+ if not isinstance(port, ArchiveUtilityPort):
1347
+ raise TypeError("port must implement ArchiveUtilityPort")
1348
+ _validate_token(port.utility_id, name="archive_utility.utility_id")
1349
+ if type(port.utility_version) is not int or port.utility_version <= 0:
1350
+ raise ValueError("archive utility version must be positive")
1351
+ require_sha256(
1352
+ port.definition_sha256,
1353
+ "archive_utility.definition_sha256",
1354
+ )
1355
+ _validate_frozen_object(benchmark, name="benchmark")
1356
+ _validate_frozen_object(archive, name="archive")
1357
+ if type(generation) is not int or generation <= 0:
1358
+ raise ValueError("generation must be positive")
1359
+ snapshot = port.freeze(
1360
+ benchmark=benchmark,
1361
+ generation=generation,
1362
+ archive=archive,
1363
+ )
1364
+ if type(snapshot) is not ArchiveUtilitySnapshot:
1365
+ raise TypeError("archive utility must return ArchiveUtilitySnapshot")
1366
+ ArchiveUtilitySnapshot.__post_init__(snapshot)
1367
+ if (
1368
+ snapshot.utility_id != port.utility_id
1369
+ or snapshot.utility_version != port.utility_version
1370
+ or snapshot.definition_sha256 != port.definition_sha256
1371
+ ):
1372
+ raise ValueError("archive utility snapshot has a foreign definition")
1373
+ if snapshot.generation != generation:
1374
+ raise ValueError("archive utility snapshot has a foreign generation")
1375
+ if snapshot.benchmark_sha256 != typed_json_sha256(benchmark):
1376
+ raise ValueError("archive utility snapshot has a foreign benchmark")
1377
+ if snapshot.archive_sha256 != typed_json_sha256(archive):
1378
+ raise ValueError("archive utility snapshot has a foreign archive cutoff")
1379
+ return snapshot
1380
+
1381
+
1382
+ @dataclass(frozen=True, slots=True)
1383
+ class CampaignPolicies:
1384
+ """Workload-independent behavioral policies injected into a campaign."""
1385
+
1386
+ cadence: CampaignCadence
1387
+ parent_selection: CampaignPolicyBinding
1388
+ memory_assignment: CampaignPolicyBinding
1389
+ portfolio_selection: CampaignPolicyBinding
1390
+ recombination: CampaignPolicyBinding
1391
+ reflection: CampaignPolicyBinding
1392
+ archive_utility: ArchiveUtilityPort
1393
+ reflection_supervision: CampaignReflectionSupervisionPolicy = field(
1394
+ default_factory=CampaignReflectionSupervisionPolicy
1395
+ )
1396
+
1397
+ def __post_init__(self) -> None:
1398
+ if not isinstance(self.cadence, CampaignCadence):
1399
+ raise TypeError("cadence must implement CampaignCadence")
1400
+ _port_record(
1401
+ _CadencePortView(self.cadence),
1402
+ role="cadence",
1403
+ )
1404
+ for name in (
1405
+ "parent_selection",
1406
+ "memory_assignment",
1407
+ "portfolio_selection",
1408
+ "recombination",
1409
+ "reflection",
1410
+ ):
1411
+ value = getattr(self, name)
1412
+ if type(value) is not CampaignPolicyBinding:
1413
+ raise TypeError(f"{name} must be an exact CampaignPolicyBinding")
1414
+ CampaignPolicyBinding.__post_init__(value)
1415
+ if type(self.reflection_supervision) is not CampaignReflectionSupervisionPolicy:
1416
+ raise TypeError(
1417
+ "reflection_supervision must be an exact "
1418
+ "CampaignReflectionSupervisionPolicy"
1419
+ )
1420
+ CampaignReflectionSupervisionPolicy.__post_init__(self.reflection_supervision)
1421
+ if not isinstance(self.archive_utility, ArchiveUtilityPort):
1422
+ raise TypeError("archive_utility must implement ArchiveUtilityPort")
1423
+ _validate_token(
1424
+ self.archive_utility.utility_id,
1425
+ name="archive_utility.utility_id",
1426
+ )
1427
+ if (
1428
+ type(self.archive_utility.utility_version) is not int
1429
+ or self.archive_utility.utility_version <= 0
1430
+ ):
1431
+ raise ValueError("archive utility version must be positive")
1432
+ require_sha256(
1433
+ self.archive_utility.definition_sha256,
1434
+ "archive_utility.definition_sha256",
1435
+ )
1436
+
1437
+ def to_record(self) -> dict[str, object]:
1438
+ self.__post_init__()
1439
+ return {
1440
+ "cadence": {
1441
+ "policy_id": self.cadence.policy_id,
1442
+ "policy_version": self.cadence.policy_version,
1443
+ "definition_sha256": self.cadence.definition_sha256,
1444
+ },
1445
+ "parent_selection": self.parent_selection.to_record(),
1446
+ "memory_assignment": self.memory_assignment.to_record(),
1447
+ "portfolio_selection": self.portfolio_selection.to_record(),
1448
+ "recombination": self.recombination.to_record(),
1449
+ "reflection": self.reflection.to_record(),
1450
+ "reflection_supervision": self.reflection_supervision.to_record(),
1451
+ "archive_utility": {
1452
+ "utility_id": self.archive_utility.utility_id,
1453
+ "utility_version": self.archive_utility.utility_version,
1454
+ "definition_sha256": self.archive_utility.definition_sha256,
1455
+ },
1456
+ }
1457
+
1458
+ @property
1459
+ def policies_sha256(self) -> str:
1460
+ return _hash(_POLICIES_DOMAIN, self.to_record())
1461
+
1462
+
1463
+ @dataclass(frozen=True, slots=True)
1464
+ class _CadencePortView:
1465
+ cadence: CampaignCadence
1466
+
1467
+ @property
1468
+ def port_id(self) -> str:
1469
+ return self.cadence.policy_id
1470
+
1471
+ @property
1472
+ def port_version(self) -> int:
1473
+ return self.cadence.policy_version
1474
+
1475
+ @property
1476
+ def definition_sha256(self) -> str:
1477
+ return self.cadence.definition_sha256
1478
+
1479
+
1480
+ @dataclass(frozen=True, slots=True)
1481
+ class CampaignAgentRuntimeRequest:
1482
+ """Identity-only runtime preflight; it intentionally carries no model config."""
1483
+
1484
+ protocol_sha256: str
1485
+ schedule_sha256: str
1486
+ session_sha256: str
1487
+ seed_batch_sha256: str
1488
+ workload_ports_sha256: str
1489
+ policies_sha256: str
1490
+ budget_sha256: str
1491
+ concurrency_sha256: str
1492
+
1493
+ def __post_init__(self) -> None:
1494
+ for name in (
1495
+ "protocol_sha256",
1496
+ "schedule_sha256",
1497
+ "session_sha256",
1498
+ "seed_batch_sha256",
1499
+ "workload_ports_sha256",
1500
+ "policies_sha256",
1501
+ "budget_sha256",
1502
+ "concurrency_sha256",
1503
+ ):
1504
+ require_sha256(getattr(self, name), name)
1505
+
1506
+ def to_record(self) -> dict[str, str]:
1507
+ self.__post_init__()
1508
+ return {
1509
+ "protocol_sha256": self.protocol_sha256,
1510
+ "schedule_sha256": self.schedule_sha256,
1511
+ "session_sha256": self.session_sha256,
1512
+ "seed_batch_sha256": self.seed_batch_sha256,
1513
+ "workload_ports_sha256": self.workload_ports_sha256,
1514
+ "policies_sha256": self.policies_sha256,
1515
+ "budget_sha256": self.budget_sha256,
1516
+ "concurrency_sha256": self.concurrency_sha256,
1517
+ }
1518
+
1519
+ @property
1520
+ def request_sha256(self) -> str:
1521
+ return _hash(_RUNTIME_REQUEST_DOMAIN, self.to_record())
1522
+
1523
+
1524
+ @dataclass(frozen=True, slots=True)
1525
+ class CampaignAgentRuntimeReceipt:
1526
+ """Runtime-owned evidence that a prepared composition is executable."""
1527
+
1528
+ request_sha256: str
1529
+ runtime_id: str
1530
+ runtime_version: int
1531
+ definition_sha256: str
1532
+ accepted: bool
1533
+ evidence: FrozenJsonObject
1534
+
1535
+ def __post_init__(self) -> None:
1536
+ require_sha256(self.request_sha256, "request_sha256")
1537
+ _validate_token(self.runtime_id, name="runtime_id")
1538
+ if type(self.runtime_version) is not int or self.runtime_version <= 0:
1539
+ raise ValueError("runtime_version must be positive")
1540
+ require_sha256(self.definition_sha256, "definition_sha256")
1541
+ if type(self.accepted) is not bool:
1542
+ raise TypeError("accepted must be an exact bool")
1543
+ _validate_frozen_object(self.evidence, name="evidence")
1544
+
1545
+ def _unsigned_record(self) -> dict[str, object]:
1546
+ return {
1547
+ "request_sha256": self.request_sha256,
1548
+ "runtime_id": self.runtime_id,
1549
+ "runtime_version": self.runtime_version,
1550
+ "definition_sha256": self.definition_sha256,
1551
+ "accepted": self.accepted,
1552
+ "evidence": thaw_json(self.evidence),
1553
+ }
1554
+
1555
+ def to_record(self) -> dict[str, object]:
1556
+ self.__post_init__()
1557
+ record = self._unsigned_record()
1558
+ return {**record, "receipt_sha256": _hash(_RUNTIME_RECEIPT_DOMAIN, record)}
1559
+
1560
+ @property
1561
+ def receipt_sha256(self) -> str:
1562
+ return str(self.to_record()["receipt_sha256"])
1563
+
1564
+
1565
+ @runtime_checkable
1566
+ class CampaignAgentRuntimePort(Protocol):
1567
+ """Agent/runtime preflight outside the workload adapter boundary."""
1568
+
1569
+ def prepare(
1570
+ self,
1571
+ request: CampaignAgentRuntimeRequest,
1572
+ ) -> CampaignAgentRuntimeReceipt: ...
1573
+
1574
+
1575
+ @runtime_checkable
1576
+ class CampaignJournalPort(Protocol):
1577
+ """Append one immutable campaign-level record."""
1578
+
1579
+ def append(self, record: FrozenJsonObject) -> None: ...
1580
+
1581
+
1582
+ @dataclass(frozen=True, slots=True)
1583
+ class PreparedEvolutionCampaign:
1584
+ """Complete, replay-identifiable result of campaign preparation."""
1585
+
1586
+ protocol: CampaignProtocol
1587
+ schedule: CampaignSchedule
1588
+ benchmark_session: CampaignBenchmarkSession
1589
+ seeds: CampaignSeedBatch
1590
+ workload_ports_sha256: str
1591
+ policies_sha256: str
1592
+ budget: OptimizerBudget
1593
+ concurrency: CampaignConcurrency
1594
+ runtime_receipt: CampaignAgentRuntimeReceipt
1595
+
1596
+ def __post_init__(self) -> None:
1597
+ if type(self.protocol) is not CampaignProtocol:
1598
+ raise TypeError("protocol must be exact")
1599
+ CampaignProtocol.__post_init__(self.protocol)
1600
+ if type(self.schedule) is not CampaignSchedule:
1601
+ raise TypeError("schedule must be exact")
1602
+ CampaignSchedule.__post_init__(self.schedule)
1603
+ if self.schedule.protocol_sha256 != self.protocol.protocol_sha256:
1604
+ raise ValueError("schedule is bound to a different protocol")
1605
+ if type(self.benchmark_session) is not CampaignBenchmarkSession:
1606
+ raise TypeError("benchmark_session must be exact")
1607
+ CampaignBenchmarkSession.__post_init__(self.benchmark_session)
1608
+ if type(self.seeds) is not CampaignSeedBatch:
1609
+ raise TypeError("seeds must be exact")
1610
+ CampaignSeedBatch.__post_init__(self.seeds)
1611
+ if self.seeds.session_sha256 != self.benchmark_session.session_sha256:
1612
+ raise ValueError("seed batch is bound to a different session")
1613
+ for name in ("workload_ports_sha256", "policies_sha256"):
1614
+ require_sha256(getattr(self, name), name)
1615
+ if type(self.budget) is not OptimizerBudget:
1616
+ raise TypeError("budget must be an exact OptimizerBudget")
1617
+ OptimizerBudget.__post_init__(self.budget)
1618
+ if type(self.concurrency) is not CampaignConcurrency:
1619
+ raise TypeError("concurrency must be exact")
1620
+ CampaignConcurrency.__post_init__(self.concurrency)
1621
+ if type(self.runtime_receipt) is not CampaignAgentRuntimeReceipt:
1622
+ raise TypeError("runtime_receipt must be exact")
1623
+ CampaignAgentRuntimeReceipt.__post_init__(self.runtime_receipt)
1624
+ expected_runtime_request = CampaignAgentRuntimeRequest(
1625
+ protocol_sha256=self.protocol.protocol_sha256,
1626
+ schedule_sha256=self.schedule.schedule_sha256,
1627
+ session_sha256=self.benchmark_session.session_sha256,
1628
+ seed_batch_sha256=self.seeds.batch_sha256,
1629
+ workload_ports_sha256=self.workload_ports_sha256,
1630
+ policies_sha256=self.policies_sha256,
1631
+ budget_sha256=self.budget.budget_hash,
1632
+ concurrency_sha256=self.concurrency.concurrency_sha256,
1633
+ )
1634
+ if (
1635
+ self.runtime_receipt.request_sha256
1636
+ != expected_runtime_request.request_sha256
1637
+ ):
1638
+ raise ValueError("runtime receipt is bound to a different preparation")
1639
+ if not self.runtime_receipt.accepted:
1640
+ raise ValueError("a prepared campaign requires runtime acceptance")
1641
+
1642
+ def _unsigned_record(self) -> dict[str, object]:
1643
+ return {
1644
+ "schema_version": 1,
1645
+ "protocol": self.protocol.to_record(),
1646
+ "protocol_sha256": self.protocol.protocol_sha256,
1647
+ "schedule": self.schedule.to_record(),
1648
+ "schedule_sha256": self.schedule.schedule_sha256,
1649
+ "benchmark_session": self.benchmark_session.to_record(),
1650
+ "benchmark_session_sha256": self.benchmark_session.session_sha256,
1651
+ "seeds": self.seeds.to_record(),
1652
+ "seed_batch_sha256": self.seeds.batch_sha256,
1653
+ "workload_ports_sha256": self.workload_ports_sha256,
1654
+ "policies_sha256": self.policies_sha256,
1655
+ "budget": self.budget.to_trace_record(),
1656
+ "budget_sha256": self.budget.budget_hash,
1657
+ "concurrency": self.concurrency.to_record(),
1658
+ "concurrency_sha256": self.concurrency.concurrency_sha256,
1659
+ "runtime_receipt": self.runtime_receipt.to_record(),
1660
+ }
1661
+
1662
+ def to_record(self) -> dict[str, object]:
1663
+ self.__post_init__()
1664
+ record = self._unsigned_record()
1665
+ return {**record, "preparation_sha256": _hash(_PREPARATION_DOMAIN, record)}
1666
+
1667
+ @property
1668
+ def preparation_sha256(self) -> str:
1669
+ return str(self.to_record()["preparation_sha256"])
1670
+
1671
+
1672
+ @dataclass(frozen=True, slots=True)
1673
+ class EvolutionCampaign:
1674
+ """Prepare one reusable campaign composition without executing live waves."""
1675
+
1676
+ protocol: CampaignProtocol
1677
+ workload: CampaignWorkloadPorts
1678
+ policies: CampaignPolicies
1679
+ runtime: CampaignAgentRuntimePort
1680
+ budget: OptimizerBudget
1681
+ concurrency: CampaignConcurrency
1682
+ journals: tuple[CampaignJournalPort, ...]
1683
+
1684
+ def __post_init__(self) -> None:
1685
+ if type(self.protocol) is not CampaignProtocol:
1686
+ raise TypeError("protocol must be an exact CampaignProtocol")
1687
+ CampaignProtocol.__post_init__(self.protocol)
1688
+ if type(self.workload) is not CampaignWorkloadPorts:
1689
+ raise TypeError("workload must be exact CampaignWorkloadPorts")
1690
+ CampaignWorkloadPorts.__post_init__(self.workload)
1691
+ if type(self.policies) is not CampaignPolicies:
1692
+ raise TypeError("policies must be exact CampaignPolicies")
1693
+ CampaignPolicies.__post_init__(self.policies)
1694
+ if not isinstance(self.runtime, CampaignAgentRuntimePort):
1695
+ raise TypeError("runtime must implement CampaignAgentRuntimePort")
1696
+ if type(self.budget) is not OptimizerBudget:
1697
+ raise TypeError("budget must be an exact OptimizerBudget")
1698
+ OptimizerBudget.__post_init__(self.budget)
1699
+ if type(self.concurrency) is not CampaignConcurrency:
1700
+ raise TypeError("concurrency must be exact CampaignConcurrency")
1701
+ CampaignConcurrency.__post_init__(self.concurrency)
1702
+ if type(self.journals) is not tuple or not self.journals:
1703
+ raise ValueError("journals must be a non-empty exact tuple")
1704
+ if any(
1705
+ not isinstance(journal, CampaignJournalPort) for journal in self.journals
1706
+ ):
1707
+ raise TypeError("every journal must implement CampaignJournalPort")
1708
+
1709
+ def prepare(self) -> PreparedEvolutionCampaign:
1710
+ """Preflight and seal a campaign; no evolutionary wave runs here."""
1711
+
1712
+ self.__post_init__()
1713
+ schedule = self.policies.cadence.build(self.protocol)
1714
+ if type(schedule) is not CampaignSchedule:
1715
+ raise TypeError("cadence must return an exact CampaignSchedule")
1716
+ CampaignSchedule.__post_init__(schedule)
1717
+ if schedule.protocol_sha256 != self.protocol.protocol_sha256:
1718
+ raise ValueError("cadence returned a schedule for a foreign protocol")
1719
+
1720
+ required_evaluations = (
1721
+ self.protocol.required_seed_count + schedule.planned_candidate_evaluations
1722
+ )
1723
+ if self.budget.max_generations < self.protocol.generation_count:
1724
+ raise ValueError("budget cannot cover the protocol generation count")
1725
+ if self.budget.max_unique_evaluations < required_evaluations:
1726
+ raise ValueError(
1727
+ "budget cannot cover the schedule's maximum candidate evaluations"
1728
+ )
1729
+ if self.budget.max_logical_llm_calls < schedule.planned_agent_calls:
1730
+ raise ValueError("budget cannot cover the schedule's planned agent calls")
1731
+
1732
+ session_request = BenchmarkSessionRequest(
1733
+ protocol_sha256=self.protocol.protocol_sha256,
1734
+ budget_sha256=self.budget.budget_hash,
1735
+ outer_seed=self.protocol.outer_seed,
1736
+ requested_evaluator_concurrency=(self.concurrency.evaluator_concurrency),
1737
+ )
1738
+ session = self.workload.benchmark.open(session_request)
1739
+ if type(session) is not CampaignBenchmarkSession:
1740
+ raise TypeError("benchmark port must return CampaignBenchmarkSession")
1741
+ CampaignBenchmarkSession.__post_init__(session)
1742
+ if session.request_sha256 != session_request.request_sha256:
1743
+ raise ValueError("benchmark session is bound to a foreign request")
1744
+ if self.concurrency.evaluator_concurrency > session.evaluator_concurrency_cap:
1745
+ raise ValueError("requested evaluator concurrency exceeds benchmark cap")
1746
+
1747
+ seeds = self.workload.seeds.load(session)
1748
+ if type(seeds) is not CampaignSeedBatch:
1749
+ raise TypeError("seed port must return CampaignSeedBatch")
1750
+ CampaignSeedBatch.__post_init__(seeds)
1751
+ if seeds.session_sha256 != session.session_sha256:
1752
+ raise ValueError("seed batch is bound to a foreign benchmark session")
1753
+ if len(seeds.seeds) != self.protocol.required_seed_count:
1754
+ raise ValueError("seed batch cardinality differs from the protocol")
1755
+
1756
+ runtime_request = CampaignAgentRuntimeRequest(
1757
+ protocol_sha256=self.protocol.protocol_sha256,
1758
+ schedule_sha256=schedule.schedule_sha256,
1759
+ session_sha256=session.session_sha256,
1760
+ seed_batch_sha256=seeds.batch_sha256,
1761
+ workload_ports_sha256=self.workload.ports_sha256,
1762
+ policies_sha256=self.policies.policies_sha256,
1763
+ budget_sha256=self.budget.budget_hash,
1764
+ concurrency_sha256=self.concurrency.concurrency_sha256,
1765
+ )
1766
+ runtime_receipt = self.runtime.prepare(runtime_request)
1767
+ if type(runtime_receipt) is not CampaignAgentRuntimeReceipt:
1768
+ raise TypeError("runtime must return CampaignAgentRuntimeReceipt")
1769
+ CampaignAgentRuntimeReceipt.__post_init__(runtime_receipt)
1770
+ if runtime_receipt.request_sha256 != runtime_request.request_sha256:
1771
+ raise ValueError("runtime receipt is bound to a foreign request")
1772
+ if not runtime_receipt.accepted:
1773
+ raise ValueError("agent runtime rejected campaign preparation")
1774
+
1775
+ prepared = PreparedEvolutionCampaign(
1776
+ protocol=self.protocol,
1777
+ schedule=schedule,
1778
+ benchmark_session=session,
1779
+ seeds=seeds,
1780
+ workload_ports_sha256=self.workload.ports_sha256,
1781
+ policies_sha256=self.policies.policies_sha256,
1782
+ budget=self.budget,
1783
+ concurrency=self.concurrency,
1784
+ runtime_receipt=runtime_receipt,
1785
+ )
1786
+ journal_record = freeze_json(prepared.to_record())
1787
+ if type(journal_record) is not FrozenJsonObject: # pragma: no cover
1788
+ raise AssertionError("campaign preparation did not freeze to an object")
1789
+ for journal in self.journals:
1790
+ journal.append(journal_record)
1791
+ return prepared
1792
+
1793
+
1794
+ __all__ = [
1795
+ "ALTERNATING_CADENCE_DEFINITION_SHA256",
1796
+ "ALTERNATING_CADENCE_POLICY_ID",
1797
+ "ALTERNATING_CADENCE_POLICY_VERSION",
1798
+ "SEALED_CUTOFF_DELAYED_CADENCE_DEFINITION_SHA256",
1799
+ "SEALED_CUTOFF_DELAYED_CADENCE_POLICY_ID",
1800
+ "SEALED_CUTOFF_DELAYED_CADENCE_POLICY_VERSION",
1801
+ "MAX_CAMPAIGN_GENERATIONS",
1802
+ "MIN_CAMPAIGN_GENERATIONS",
1803
+ "AlternatingPortfolioRecombinationCadence",
1804
+ "ArchiveUtilityPort",
1805
+ "ArchiveUtilitySnapshot",
1806
+ "BenchmarkSessionRequest",
1807
+ "CampaignAgentRuntimePort",
1808
+ "CampaignAgentRuntimeReceipt",
1809
+ "CampaignAgentRuntimeRequest",
1810
+ "CampaignBenchmarkPort",
1811
+ "CampaignBenchmarkSession",
1812
+ "CampaignCadence",
1813
+ "CampaignCatalogPort",
1814
+ "CampaignConcurrency",
1815
+ "CampaignEvidencePort",
1816
+ "CampaignGenerationKind",
1817
+ "CampaignGenerationPair",
1818
+ "CampaignGenerationStep",
1819
+ "CampaignJournalPort",
1820
+ "CampaignPolicies",
1821
+ "CampaignPolicyBinding",
1822
+ "CampaignPromotionBarrier",
1823
+ "CampaignProtocol",
1824
+ "CampaignReflectionWave",
1825
+ "CampaignReflectionSupervisionPolicy",
1826
+ "CampaignSchedule",
1827
+ "CampaignSeed",
1828
+ "CampaignSeedBatch",
1829
+ "CampaignSeedPort",
1830
+ "CampaignWorkloadPorts",
1831
+ "EvolutionCampaign",
1832
+ "ParentVariationBinding",
1833
+ "PreparedEvolutionCampaign",
1834
+ "ReflectionLaunchMode",
1835
+ "ReflectionFailureMode",
1836
+ "TerminalReflectionPolicy",
1837
+ "ReflectionVisibility",
1838
+ "SealedCutoffDelayedAdmissionCadence",
1839
+ "freeze_archive_utility",
1840
+ ]