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,811 @@
1
+ """Small event-auditable Pareto archive for explicit agentic evolution.
2
+
3
+ The archive is deliberately separate from generation policy. It consumes evaluated
4
+ ``EvolutionCandidate`` occurrences, applies explicit admissibility gates, deduplicates
5
+ configurations across the whole run, and maintains one deterministic representative
6
+ for exact objective-vector ties. Every state transition is returned as an immutable
7
+ decision that a runner can emit directly into its trace.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import hashlib
13
+ import json
14
+ from collections.abc import Sequence
15
+ from dataclasses import dataclass
16
+ from enum import Enum
17
+ from numbers import Real
18
+
19
+ from agent_evolve.application.agentic_evolution import EvolutionCandidate
20
+ from agent_evolve.application.outcome_relation import (
21
+ OutcomeRelation,
22
+ OutcomeRelationPolicyBinding,
23
+ objective_pareto_outcome_binding,
24
+ )
25
+ from agent_evolve.core.problem import (
26
+ ObjectiveSpec,
27
+ ProblemContractError,
28
+ normalize_objective_values,
29
+ validate_objective_specs,
30
+ )
31
+ from agent_evolve.core.results import dominates
32
+ from agent_evolve.domain.ids import CandidateId
33
+ from agent_evolve.domain.patch import require_sha256
34
+
35
+
36
+ _CANDIDATE_HASH_DOMAIN = b"agent-evolve:pareto-candidate:v1\x00"
37
+
38
+
39
+ class ParetoDecisionAction(str, Enum):
40
+ """The archive state transition described by one decision."""
41
+
42
+ ADMITTED = "admitted"
43
+ REMOVED = "removed"
44
+ REJECTED = "rejected"
45
+
46
+
47
+ class ParetoDecisionReason(str, Enum):
48
+ """Machine-stable explanations for every archive decision."""
49
+
50
+ ADMITTED_NONDOMINATED = "admitted_nondominated"
51
+ ADMITTED_TIE_BREAK_REPLACEMENT = "admitted_tie_break_replacement"
52
+ REMOVED_DOMINATED = "removed_dominated"
53
+ REMOVED_TIE_BREAK = "removed_tie_break"
54
+ REJECTED_INVALID = "rejected_invalid"
55
+ REJECTED_OPERATOR_NONCOMPLIANT = "rejected_operator_noncompliant"
56
+ REJECTED_EVIDENCE_NONCOMPLIANT = "rejected_evidence_noncompliant"
57
+ REJECTED_OBJECTIVE_CONTRACT = "rejected_objective_contract"
58
+ REJECTED_DUPLICATE_CANDIDATE = "rejected_duplicate_candidate"
59
+ REJECTED_CANDIDATE_ID_CONFLICT = "rejected_candidate_id_conflict"
60
+ REJECTED_DUPLICATE_CONFIGURATION = "rejected_duplicate_configuration"
61
+ REJECTED_DOMINATED = "rejected_dominated"
62
+ REJECTED_OBJECTIVE_TIE = "rejected_objective_tie"
63
+ ADMITTED_RELATION_FRONT = "admitted_relation_front"
64
+ ADMITTED_EQUIVALENCE_TIE_BREAK = "admitted_equivalence_tie_break"
65
+ REMOVED_WORSE_RELATION = "removed_worse_relation"
66
+ REMOVED_EQUIVALENCE_TIE_BREAK = "removed_equivalence_tie_break"
67
+ REJECTED_WORSE_RELATION = "rejected_worse_relation"
68
+ REJECTED_EQUIVALENCE = "rejected_equivalence"
69
+
70
+
71
+ class EvidenceAdmissionPolicy(str, Enum):
72
+ """Whether model-authored annotation errors can exclude objective evidence."""
73
+
74
+ REQUIRE_COMPLIANT = "require_compliant"
75
+ RECORD_ONLY = "record_only"
76
+
77
+
78
+ def _objective_hash_values(candidate: EvolutionCandidate) -> list[list[str]]:
79
+ """Return a deterministic projection even when objectives fail later checks."""
80
+
81
+ if type(candidate.objectives) is not tuple:
82
+ return [["<invalid-container>", type(candidate.objectives).__qualname__]]
83
+ projected: list[list[str]] = []
84
+ for item in candidate.objectives:
85
+ if type(item) is not tuple or len(item) != 2:
86
+ projected.append(["<invalid-item>", type(item).__qualname__])
87
+ continue
88
+ name, value = item
89
+ name_token = name if type(name) is str else f"<{type(name).__qualname__}>"
90
+ if isinstance(value, Real) and not isinstance(value, bool):
91
+ try:
92
+ value_token = float(value).hex()
93
+ except (OverflowError, TypeError, ValueError):
94
+ value_token = f"<invalid-{type(value).__qualname__}>"
95
+ else:
96
+ value_token = f"<{type(value).__qualname__}>"
97
+ projected.append([name_token, value_token])
98
+ return projected
99
+
100
+
101
+ def pareto_candidate_hash(candidate: EvolutionCandidate) -> str:
102
+ """Hash the occurrence and all archive-relevant candidate facts.
103
+
104
+ This is an archive projection hash, not a replacement for ``CandidateId`` or the
105
+ canonical typed-JSON configuration hash. Including both identity and gate facts
106
+ makes accidental reuse of one occurrence ID with different evaluation evidence
107
+ visible in the decision stream.
108
+ """
109
+
110
+ if type(candidate) is not EvolutionCandidate:
111
+ raise TypeError("candidate must be an exact EvolutionCandidate")
112
+ EvolutionCandidate.__post_init__(candidate)
113
+ occurrence = candidate.occurrence
114
+ record = {
115
+ "candidate_id": candidate.candidate_id.value,
116
+ "configuration_artifact_hash": occurrence.configuration_artifact_hash,
117
+ "configuration_hash": occurrence.configuration_hash,
118
+ "evidence_compliant": candidate.evidence_compliant,
119
+ "objectives": _objective_hash_values(candidate),
120
+ "operator_compliant": candidate.operator_compliant,
121
+ "operator_invocation_id": (
122
+ None
123
+ if occurrence.operator_invocation_id is None
124
+ else occurrence.operator_invocation_id.value
125
+ ),
126
+ "proposal_sequence": occurrence.proposal_sequence,
127
+ "valid": candidate.valid,
128
+ }
129
+ if candidate.detailed_evaluation is not None:
130
+ record["detailed_evaluation_sha256"] = (
131
+ candidate.detailed_evaluation.evidence_sha256
132
+ )
133
+ if candidate.objective_resolution_receipt is not None:
134
+ record["objective_resolution_receipt_sha256"] = (
135
+ candidate.objective_resolution_receipt.receipt_sha256
136
+ )
137
+ encoded = json.dumps(
138
+ record,
139
+ ensure_ascii=True,
140
+ separators=(",", ":"),
141
+ sort_keys=True,
142
+ ).encode("utf-8")
143
+ return hashlib.sha256(_CANDIDATE_HASH_DOMAIN + encoded).hexdigest()
144
+
145
+
146
+ @dataclass(frozen=True, slots=True)
147
+ class ParetoCandidateRef:
148
+ """Minimal immutable identity used in decisions and trace records."""
149
+
150
+ candidate_id: CandidateId
151
+ candidate_hash: str
152
+ configuration_hash: str
153
+
154
+ def __post_init__(self) -> None:
155
+ if type(self.candidate_id) is not CandidateId:
156
+ raise TypeError("candidate_id must be an exact CandidateId")
157
+ CandidateId.__post_init__(self.candidate_id)
158
+ require_sha256(self.candidate_hash, "candidate_hash")
159
+ require_sha256(self.configuration_hash, "configuration_hash")
160
+
161
+ def to_trace_record(self) -> dict[str, str]:
162
+ """Return a fresh JSON-safe trace projection."""
163
+
164
+ return {
165
+ "candidate_id": self.candidate_id.value,
166
+ "candidate_hash": self.candidate_hash,
167
+ "configuration_hash": self.configuration_hash,
168
+ }
169
+
170
+
171
+ def _candidate_ref(candidate: EvolutionCandidate) -> ParetoCandidateRef:
172
+ return ParetoCandidateRef(
173
+ candidate_id=candidate.candidate_id,
174
+ candidate_hash=pareto_candidate_hash(candidate),
175
+ configuration_hash=candidate.occurrence.configuration_hash,
176
+ )
177
+
178
+
179
+ def _ref_key(reference: ParetoCandidateRef) -> tuple[str, str, str]:
180
+ return (
181
+ reference.configuration_hash,
182
+ reference.candidate_id.value,
183
+ reference.candidate_hash,
184
+ )
185
+
186
+
187
+ @dataclass(frozen=True, slots=True)
188
+ class ArchiveRelationEvidence:
189
+ """Relation of the considered candidate to one incumbent front member."""
190
+
191
+ incumbent: ParetoCandidateRef
192
+ relation: OutcomeRelation
193
+
194
+ def __post_init__(self) -> None:
195
+ if type(self.incumbent) is not ParetoCandidateRef:
196
+ raise TypeError("incumbent must be an exact ParetoCandidateRef")
197
+ if type(self.relation) is not OutcomeRelation:
198
+ raise TypeError("relation must be an OutcomeRelation")
199
+
200
+ def to_trace_record(self) -> dict[str, object]:
201
+ return {
202
+ "incumbent": self.incumbent.to_trace_record(),
203
+ "candidate_relation": self.relation.value,
204
+ }
205
+
206
+
207
+ @dataclass(frozen=True, slots=True)
208
+ class ParetoDecision:
209
+ """One immutable, self-identifying archive decision."""
210
+
211
+ decision_sequence: int
212
+ consideration_sequence: int
213
+ action: ParetoDecisionAction
214
+ reasons: tuple[ParetoDecisionReason, ...]
215
+ candidate: ParetoCandidateRef
216
+ failure_details: tuple[tuple[ParetoDecisionReason, str], ...] = ()
217
+ dominators: tuple[ParetoCandidateRef, ...] = ()
218
+ removed_candidates: tuple[ParetoCandidateRef, ...] = ()
219
+ tie_with: tuple[ParetoCandidateRef, ...] = ()
220
+ duplicate_of: ParetoCandidateRef | None = None
221
+ caused_by: ParetoCandidateRef | None = None
222
+ front_after: tuple[ParetoCandidateRef, ...] = ()
223
+ outcome_relations: tuple[ArchiveRelationEvidence, ...] = ()
224
+
225
+ def __post_init__(self) -> None:
226
+ if type(self.decision_sequence) is not int or self.decision_sequence <= 0:
227
+ raise ValueError("decision_sequence must be a positive exact integer")
228
+ if (
229
+ type(self.consideration_sequence) is not int
230
+ or self.consideration_sequence <= 0
231
+ ):
232
+ raise ValueError("consideration_sequence must be a positive exact integer")
233
+ if type(self.action) is not ParetoDecisionAction:
234
+ raise TypeError("action must be a ParetoDecisionAction")
235
+ if type(self.reasons) is not tuple or not self.reasons or any(
236
+ type(reason) is not ParetoDecisionReason for reason in self.reasons
237
+ ):
238
+ raise ValueError("reasons must be a non-empty exact reason tuple")
239
+ if type(self.candidate) is not ParetoCandidateRef:
240
+ raise TypeError("candidate must be a ParetoCandidateRef")
241
+ for name in ("dominators", "removed_candidates", "tie_with", "front_after"):
242
+ references = getattr(self, name)
243
+ if type(references) is not tuple or any(
244
+ type(reference) is not ParetoCandidateRef for reference in references
245
+ ):
246
+ raise TypeError(f"{name} must be an exact ParetoCandidateRef tuple")
247
+ if self.duplicate_of is not None and type(
248
+ self.duplicate_of
249
+ ) is not ParetoCandidateRef:
250
+ raise TypeError("duplicate_of must be a ParetoCandidateRef or None")
251
+ if self.caused_by is not None and type(self.caused_by) is not ParetoCandidateRef:
252
+ raise TypeError("caused_by must be a ParetoCandidateRef or None")
253
+ if type(self.failure_details) is not tuple:
254
+ raise TypeError("failure_details must be an exact tuple")
255
+ for reason, detail in self.failure_details:
256
+ if type(reason) is not ParetoDecisionReason or type(detail) is not str:
257
+ raise TypeError("failure details must contain exact reason/text pairs")
258
+ if type(self.outcome_relations) is not tuple or any(
259
+ type(item) is not ArchiveRelationEvidence
260
+ for item in self.outcome_relations
261
+ ):
262
+ raise TypeError(
263
+ "outcome_relations must contain exact ArchiveRelationEvidence values"
264
+ )
265
+
266
+ def to_trace_record(self) -> dict[str, object]:
267
+ """Return a fresh JSON-safe event suitable for a runner trace sink."""
268
+
269
+ record = {
270
+ "event_type": "pareto_archive_decision",
271
+ "decision_sequence": self.decision_sequence,
272
+ "consideration_sequence": self.consideration_sequence,
273
+ "action": self.action.value,
274
+ "reasons": [reason.value for reason in self.reasons],
275
+ **self.candidate.to_trace_record(),
276
+ "failure_details": [
277
+ {"reason": reason.value, "detail": detail}
278
+ for reason, detail in self.failure_details
279
+ ],
280
+ "dominators": [item.to_trace_record() for item in self.dominators],
281
+ "removed_candidates": [
282
+ item.to_trace_record() for item in self.removed_candidates
283
+ ],
284
+ "tie_with": [item.to_trace_record() for item in self.tie_with],
285
+ "duplicate_of": (
286
+ None if self.duplicate_of is None else self.duplicate_of.to_trace_record()
287
+ ),
288
+ "caused_by": (
289
+ None if self.caused_by is None else self.caused_by.to_trace_record()
290
+ ),
291
+ "front_after": [item.to_trace_record() for item in self.front_after],
292
+ }
293
+ if self.outcome_relations:
294
+ record["outcome_relations"] = [
295
+ item.to_trace_record() for item in self.outcome_relations
296
+ ]
297
+ return record
298
+
299
+
300
+ @dataclass(frozen=True, slots=True)
301
+ class ParetoArchiveSnapshot:
302
+ """Immutable view of archive state and its complete decision ledger."""
303
+
304
+ objectives: tuple[ObjectiveSpec, ...]
305
+ front_candidates: tuple[EvolutionCandidate, ...]
306
+ front_references: tuple[ParetoCandidateRef, ...]
307
+ decisions: tuple[ParetoDecision, ...]
308
+ consideration_count: int
309
+ eligible_configuration_count: int
310
+ evidence_admission_policy: EvidenceAdmissionPolicy
311
+ outcome_relation_policy: tuple[str, int, str]
312
+ objective_pareto_relation: bool
313
+
314
+ def to_trace_record(self) -> dict[str, object]:
315
+ """Return aggregate JSON-safe evidence without copying full configurations."""
316
+
317
+ record = {
318
+ "event_type": "pareto_archive_snapshot",
319
+ "objectives": [
320
+ {"name": objective.name, "goal": objective.goal}
321
+ for objective in self.objectives
322
+ ],
323
+ "front": [item.to_trace_record() for item in self.front_references],
324
+ "front_size": len(self.front_references),
325
+ "consideration_count": self.consideration_count,
326
+ "eligible_configuration_count": self.eligible_configuration_count,
327
+ "decision_count": len(self.decisions),
328
+ "evidence_admission_policy": self.evidence_admission_policy.value,
329
+ }
330
+ if not self.objective_pareto_relation:
331
+ record["outcome_relation_policy"] = {
332
+ "policy_id": self.outcome_relation_policy[0],
333
+ "policy_version": self.outcome_relation_policy[1],
334
+ "definition_sha256": self.outcome_relation_policy[2],
335
+ }
336
+ return record
337
+
338
+
339
+ class ParetoArchive:
340
+ """Admissibility-gated, deterministic, event-auditable Pareto archive."""
341
+
342
+ def __init__(
343
+ self,
344
+ objectives: Sequence[ObjectiveSpec],
345
+ *,
346
+ evidence_admission_policy: EvidenceAdmissionPolicy = (
347
+ EvidenceAdmissionPolicy.REQUIRE_COMPLIANT
348
+ ),
349
+ outcome_relation_binding: OutcomeRelationPolicyBinding | None = None,
350
+ ) -> None:
351
+ objective_tuple = tuple(objectives)
352
+ validate_objective_specs(objective_tuple)
353
+ if type(evidence_admission_policy) is not EvidenceAdmissionPolicy:
354
+ raise TypeError(
355
+ "evidence_admission_policy must be an EvidenceAdmissionPolicy"
356
+ )
357
+ default_relation = objective_pareto_outcome_binding(objective_tuple)
358
+ relation = (
359
+ default_relation
360
+ if outcome_relation_binding is None
361
+ else outcome_relation_binding
362
+ )
363
+ if type(relation) is not OutcomeRelationPolicyBinding:
364
+ raise TypeError(
365
+ "outcome_relation_binding must be an OutcomeRelationPolicyBinding"
366
+ )
367
+ OutcomeRelationPolicyBinding.__post_init__(relation)
368
+ self._objectives = objective_tuple
369
+ self._evidence_admission_policy = evidence_admission_policy
370
+ self.outcome_relation_binding = relation
371
+ self._objective_pareto_relation = relation.identity == default_relation.identity
372
+ self._front: dict[str, EvolutionCandidate] = {}
373
+ self._front_objectives: dict[str, dict[str, float]] = {}
374
+ self._observed_ids: dict[str, ParetoCandidateRef] = {}
375
+ self._seen_configurations: dict[str, ParetoCandidateRef] = {}
376
+ self._decisions: list[ParetoDecision] = []
377
+ self._consideration_count = 0
378
+
379
+ @property
380
+ def objectives(self) -> tuple[ObjectiveSpec, ...]:
381
+ return self._objectives
382
+
383
+ @property
384
+ def front(self) -> tuple[EvolutionCandidate, ...]:
385
+ return self._ordered_front()[0]
386
+
387
+ @property
388
+ def decisions(self) -> tuple[ParetoDecision, ...]:
389
+ return tuple(self._decisions)
390
+
391
+ def __len__(self) -> int:
392
+ return len(self._front)
393
+
394
+ def _ordered_front(
395
+ self,
396
+ ) -> tuple[tuple[EvolutionCandidate, ...], tuple[ParetoCandidateRef, ...]]:
397
+ pairs = [
398
+ (candidate, _candidate_ref(candidate)) for candidate in self._front.values()
399
+ ]
400
+ pairs.sort(key=lambda pair: _ref_key(pair[1]))
401
+ return (
402
+ tuple(candidate for candidate, _ in pairs),
403
+ tuple(reference for _, reference in pairs),
404
+ )
405
+
406
+ def _append_decision(
407
+ self,
408
+ *,
409
+ action: ParetoDecisionAction,
410
+ reasons: tuple[ParetoDecisionReason, ...],
411
+ candidate: ParetoCandidateRef,
412
+ failure_details: tuple[tuple[ParetoDecisionReason, str], ...] = (),
413
+ dominators: tuple[ParetoCandidateRef, ...] = (),
414
+ removed_candidates: tuple[ParetoCandidateRef, ...] = (),
415
+ tie_with: tuple[ParetoCandidateRef, ...] = (),
416
+ duplicate_of: ParetoCandidateRef | None = None,
417
+ caused_by: ParetoCandidateRef | None = None,
418
+ outcome_relations: tuple[ArchiveRelationEvidence, ...] = (),
419
+ ) -> ParetoDecision:
420
+ front_after = self._ordered_front()[1]
421
+ decision = ParetoDecision(
422
+ decision_sequence=len(self._decisions) + 1,
423
+ consideration_sequence=self._consideration_count,
424
+ action=action,
425
+ reasons=reasons,
426
+ candidate=candidate,
427
+ failure_details=failure_details,
428
+ dominators=tuple(sorted(dominators, key=_ref_key)),
429
+ removed_candidates=tuple(sorted(removed_candidates, key=_ref_key)),
430
+ tie_with=tuple(sorted(tie_with, key=_ref_key)),
431
+ duplicate_of=duplicate_of,
432
+ caused_by=caused_by,
433
+ front_after=front_after,
434
+ outcome_relations=outcome_relations,
435
+ )
436
+ self._decisions.append(decision)
437
+ return decision
438
+
439
+ @staticmethod
440
+ def _failure_detail(message: str | None, fallback: str) -> str:
441
+ if type(message) is str and message.strip():
442
+ return message
443
+ return fallback
444
+
445
+ def _normalize_candidate_objectives(
446
+ self,
447
+ candidate: EvolutionCandidate,
448
+ ) -> dict[str, float]:
449
+ if type(candidate.objectives) is not tuple:
450
+ raise ProblemContractError("candidate objectives must be an exact tuple")
451
+ raw: dict[str, object] = {}
452
+ for item in candidate.objectives:
453
+ if type(item) is not tuple or len(item) != 2:
454
+ raise ProblemContractError(
455
+ "candidate objectives must contain exact (name, value) tuples"
456
+ )
457
+ name, value = item
458
+ if type(name) is not str:
459
+ raise ProblemContractError("candidate objective names must be strings")
460
+ if name in raw:
461
+ raise ProblemContractError(f"duplicate candidate objective {name!r}")
462
+ raw[name] = value
463
+ return normalize_objective_values(raw, self._objectives)
464
+
465
+ def _consider_by_outcome_relation(
466
+ self,
467
+ candidate: EvolutionCandidate,
468
+ reference: ParetoCandidateRef,
469
+ objective_map: dict[str, float],
470
+ front_entries: list[
471
+ tuple[EvolutionCandidate, ParetoCandidateRef, dict[str, float]]
472
+ ],
473
+ *,
474
+ start: int,
475
+ ) -> tuple[ParetoDecision, ...]:
476
+ detailed = candidate.detailed_evaluation
477
+ if detailed is None:
478
+ raise ProblemContractError(
479
+ "custom outcome relation requires candidate detailed evidence"
480
+ )
481
+ relations: list[
482
+ tuple[EvolutionCandidate, ParetoCandidateRef, OutcomeRelation]
483
+ ] = []
484
+ for incumbent, incumbent_ref, _ in sorted(
485
+ front_entries,
486
+ key=lambda entry: _ref_key(entry[1]),
487
+ ):
488
+ incumbent_detailed = incumbent.detailed_evaluation
489
+ if incumbent_detailed is None:
490
+ raise RuntimeError(
491
+ "custom-relation archive contains evidence-free incumbent"
492
+ )
493
+ relations.append(
494
+ (
495
+ incumbent,
496
+ incumbent_ref,
497
+ self.outcome_relation_binding.relate(
498
+ detailed,
499
+ incumbent_detailed,
500
+ ),
501
+ )
502
+ )
503
+ relation_evidence = tuple(
504
+ ArchiveRelationEvidence(incumbent_ref, relation)
505
+ for _, incumbent_ref, relation in relations
506
+ )
507
+ worse = tuple(
508
+ incumbent_ref
509
+ for _, incumbent_ref, relation in relations
510
+ if relation is OutcomeRelation.WORSE
511
+ )
512
+ if worse:
513
+ self._append_decision(
514
+ action=ParetoDecisionAction.REJECTED,
515
+ reasons=(ParetoDecisionReason.REJECTED_WORSE_RELATION,),
516
+ candidate=reference,
517
+ outcome_relations=relation_evidence,
518
+ )
519
+ return tuple(self._decisions[start:])
520
+
521
+ equivalent = tuple(
522
+ incumbent_ref
523
+ for _, incumbent_ref, relation in relations
524
+ if relation is OutcomeRelation.EQUIVALENT
525
+ )
526
+ if equivalent and min((reference, *equivalent), key=_ref_key) is not reference:
527
+ self._append_decision(
528
+ action=ParetoDecisionAction.REJECTED,
529
+ reasons=(ParetoDecisionReason.REJECTED_EQUIVALENCE,),
530
+ candidate=reference,
531
+ tie_with=equivalent,
532
+ outcome_relations=relation_evidence,
533
+ )
534
+ return tuple(self._decisions[start:])
535
+
536
+ better = tuple(
537
+ incumbent_ref
538
+ for _, incumbent_ref, relation in relations
539
+ if relation is OutcomeRelation.BETTER
540
+ )
541
+ removed = tuple(
542
+ sorted(
543
+ {
544
+ item.candidate_id.value: item
545
+ for item in (*equivalent, *better)
546
+ }.values(),
547
+ key=_ref_key,
548
+ )
549
+ )
550
+ equivalent_ids = {item.candidate_id.value for item in equivalent}
551
+ for item in removed:
552
+ del self._front[item.candidate_id.value]
553
+ del self._front_objectives[item.candidate_id.value]
554
+ self._front[candidate.candidate_id.value] = candidate
555
+ self._front_objectives[candidate.candidate_id.value] = objective_map
556
+
557
+ self._append_decision(
558
+ action=ParetoDecisionAction.ADMITTED,
559
+ reasons=(
560
+ ParetoDecisionReason.ADMITTED_EQUIVALENCE_TIE_BREAK
561
+ if equivalent
562
+ else ParetoDecisionReason.ADMITTED_RELATION_FRONT
563
+ ,),
564
+ candidate=reference,
565
+ removed_candidates=removed,
566
+ tie_with=equivalent,
567
+ outcome_relations=relation_evidence,
568
+ )
569
+ for item in removed:
570
+ self._append_decision(
571
+ action=ParetoDecisionAction.REMOVED,
572
+ reasons=(
573
+ ParetoDecisionReason.REMOVED_EQUIVALENCE_TIE_BREAK
574
+ if item.candidate_id.value in equivalent_ids
575
+ else ParetoDecisionReason.REMOVED_WORSE_RELATION
576
+ ,),
577
+ candidate=item,
578
+ caused_by=reference,
579
+ )
580
+ return tuple(self._decisions[start:])
581
+
582
+ def consider(self, candidate: EvolutionCandidate) -> tuple[ParetoDecision, ...]:
583
+ """Consider one candidate and return every resulting atomic decision.
584
+
585
+ An admission that evicts two incumbents returns three decisions: one admission
586
+ naming both removals, followed by one explicit removal decision per incumbent.
587
+ Rejections never mutate the Pareto front. Only gate-passing candidates consume
588
+ configuration-deduplication identities.
589
+ """
590
+
591
+ reference = _candidate_ref(candidate)
592
+ self._consideration_count += 1
593
+ start = len(self._decisions)
594
+
595
+ previous_id = self._observed_ids.get(candidate.candidate_id.value)
596
+ if previous_id is not None:
597
+ reason = (
598
+ ParetoDecisionReason.REJECTED_DUPLICATE_CANDIDATE
599
+ if previous_id.candidate_hash == reference.candidate_hash
600
+ else ParetoDecisionReason.REJECTED_CANDIDATE_ID_CONFLICT
601
+ )
602
+ self._append_decision(
603
+ action=ParetoDecisionAction.REJECTED,
604
+ reasons=(reason,),
605
+ candidate=reference,
606
+ duplicate_of=previous_id,
607
+ )
608
+ return tuple(self._decisions[start:])
609
+ self._observed_ids[candidate.candidate_id.value] = reference
610
+
611
+ gate_reasons: list[ParetoDecisionReason] = []
612
+ failure_details: list[tuple[ParetoDecisionReason, str]] = []
613
+ if not candidate.valid:
614
+ reason = ParetoDecisionReason.REJECTED_INVALID
615
+ gate_reasons.append(reason)
616
+ failure_details.append(
617
+ (
618
+ reason,
619
+ self._failure_detail(
620
+ candidate.failure_message,
621
+ "candidate.valid is False",
622
+ ),
623
+ )
624
+ )
625
+ if not candidate.operator_compliant:
626
+ reason = ParetoDecisionReason.REJECTED_OPERATOR_NONCOMPLIANT
627
+ gate_reasons.append(reason)
628
+ failure_details.append(
629
+ (
630
+ reason,
631
+ self._failure_detail(
632
+ candidate.operator_failure,
633
+ "candidate.operator_compliant is False",
634
+ ),
635
+ )
636
+ )
637
+ if (
638
+ self._evidence_admission_policy
639
+ is EvidenceAdmissionPolicy.REQUIRE_COMPLIANT
640
+ and not candidate.evidence_compliant
641
+ ):
642
+ reason = ParetoDecisionReason.REJECTED_EVIDENCE_NONCOMPLIANT
643
+ gate_reasons.append(reason)
644
+ failure_details.append(
645
+ (
646
+ reason,
647
+ self._failure_detail(
648
+ candidate.evidence_failure,
649
+ "candidate.evidence_compliant is False",
650
+ ),
651
+ )
652
+ )
653
+ if gate_reasons:
654
+ self._append_decision(
655
+ action=ParetoDecisionAction.REJECTED,
656
+ reasons=tuple(gate_reasons),
657
+ candidate=reference,
658
+ failure_details=tuple(failure_details),
659
+ )
660
+ return tuple(self._decisions[start:])
661
+
662
+ try:
663
+ objective_map = self._normalize_candidate_objectives(candidate)
664
+ except (OverflowError, ProblemContractError, TypeError, ValueError) as exc:
665
+ reason = ParetoDecisionReason.REJECTED_OBJECTIVE_CONTRACT
666
+ self._append_decision(
667
+ action=ParetoDecisionAction.REJECTED,
668
+ reasons=(reason,),
669
+ candidate=reference,
670
+ failure_details=((reason, str(exc)),),
671
+ )
672
+ return tuple(self._decisions[start:])
673
+
674
+ duplicate = self._seen_configurations.get(reference.configuration_hash)
675
+ if duplicate is not None:
676
+ self._append_decision(
677
+ action=ParetoDecisionAction.REJECTED,
678
+ reasons=(
679
+ ParetoDecisionReason.REJECTED_DUPLICATE_CONFIGURATION,
680
+ ),
681
+ candidate=reference,
682
+ duplicate_of=duplicate,
683
+ )
684
+ return tuple(self._decisions[start:])
685
+ self._seen_configurations[reference.configuration_hash] = reference
686
+
687
+ front_entries = [
688
+ (
689
+ incumbent,
690
+ _candidate_ref(incumbent),
691
+ self._front_objectives[incumbent.candidate_id.value],
692
+ )
693
+ for incumbent in self._front.values()
694
+ ]
695
+ if not self._objective_pareto_relation:
696
+ return self._consider_by_outcome_relation(
697
+ candidate,
698
+ reference,
699
+ objective_map,
700
+ front_entries,
701
+ start=start,
702
+ )
703
+ dominators = tuple(
704
+ incumbent_ref
705
+ for _, incumbent_ref, incumbent_objectives in front_entries
706
+ if dominates(incumbent_objectives, objective_map, self._objectives)
707
+ )
708
+ if dominators:
709
+ self._append_decision(
710
+ action=ParetoDecisionAction.REJECTED,
711
+ reasons=(ParetoDecisionReason.REJECTED_DOMINATED,),
712
+ candidate=reference,
713
+ dominators=dominators,
714
+ )
715
+ return tuple(self._decisions[start:])
716
+
717
+ objective_tuple = tuple(objective_map[spec.name] for spec in self._objectives)
718
+ tied = tuple(
719
+ incumbent_ref
720
+ for _, incumbent_ref, incumbent_objectives in front_entries
721
+ if tuple(
722
+ incumbent_objectives[spec.name] for spec in self._objectives
723
+ )
724
+ == objective_tuple
725
+ )
726
+ if tied and min((reference, *tied), key=_ref_key) is not reference:
727
+ self._append_decision(
728
+ action=ParetoDecisionAction.REJECTED,
729
+ reasons=(ParetoDecisionReason.REJECTED_OBJECTIVE_TIE,),
730
+ candidate=reference,
731
+ tie_with=tied,
732
+ )
733
+ return tuple(self._decisions[start:])
734
+
735
+ dominated = tuple(
736
+ incumbent_ref
737
+ for _, incumbent_ref, incumbent_objectives in front_entries
738
+ if dominates(objective_map, incumbent_objectives, self._objectives)
739
+ )
740
+ tied_ids = {item.candidate_id.value for item in tied}
741
+ dominated_ids = {item.candidate_id.value for item in dominated}
742
+ removed = tuple(
743
+ sorted(
744
+ {
745
+ item.candidate_id.value: item
746
+ for item in (*tied, *dominated)
747
+ }.values(),
748
+ key=_ref_key,
749
+ )
750
+ )
751
+ for item in removed:
752
+ del self._front[item.candidate_id.value]
753
+ del self._front_objectives[item.candidate_id.value]
754
+ self._front[candidate.candidate_id.value] = candidate
755
+ self._front_objectives[candidate.candidate_id.value] = objective_map
756
+
757
+ admission_reason = (
758
+ ParetoDecisionReason.ADMITTED_TIE_BREAK_REPLACEMENT
759
+ if tied
760
+ else ParetoDecisionReason.ADMITTED_NONDOMINATED
761
+ )
762
+ self._append_decision(
763
+ action=ParetoDecisionAction.ADMITTED,
764
+ reasons=(admission_reason,),
765
+ candidate=reference,
766
+ removed_candidates=removed,
767
+ tie_with=tied,
768
+ )
769
+ for item in removed:
770
+ removal_reason = (
771
+ ParetoDecisionReason.REMOVED_TIE_BREAK
772
+ if item.candidate_id.value in tied_ids
773
+ else ParetoDecisionReason.REMOVED_DOMINATED
774
+ )
775
+ if item.candidate_id.value not in tied_ids | dominated_ids:
776
+ raise AssertionError("archive removal lacks a deterministic cause")
777
+ self._append_decision(
778
+ action=ParetoDecisionAction.REMOVED,
779
+ reasons=(removal_reason,),
780
+ candidate=item,
781
+ caused_by=reference,
782
+ )
783
+ return tuple(self._decisions[start:])
784
+
785
+ def snapshot(self) -> ParetoArchiveSnapshot:
786
+ """Return an immutable point-in-time view of state and all decisions."""
787
+
788
+ candidates, references = self._ordered_front()
789
+ return ParetoArchiveSnapshot(
790
+ objectives=self._objectives,
791
+ front_candidates=candidates,
792
+ front_references=references,
793
+ decisions=tuple(self._decisions),
794
+ consideration_count=self._consideration_count,
795
+ eligible_configuration_count=len(self._seen_configurations),
796
+ evidence_admission_policy=self._evidence_admission_policy,
797
+ outcome_relation_policy=self.outcome_relation_binding.identity,
798
+ objective_pareto_relation=self._objective_pareto_relation,
799
+ )
800
+
801
+
802
+ __all__ = [
803
+ "EvidenceAdmissionPolicy",
804
+ "ParetoArchive",
805
+ "ParetoArchiveSnapshot",
806
+ "ParetoCandidateRef",
807
+ "ParetoDecision",
808
+ "ParetoDecisionAction",
809
+ "ParetoDecisionReason",
810
+ "pareto_candidate_hash",
811
+ ]