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,1303 @@
1
+ """V9 candidate composition: v8lite_r2 plus config-gated R1/R2/R3 arms.
2
+
3
+ The frozen ``v8lite_r2`` composition stays the reference; this module
4
+ never modifies it. Three independently config-gated refinements from the
5
+ jul28 pareto defect theory compose OVER it, each ablatable on its own:
6
+
7
+ * R1 (``region_conditional_credit``) — the continuation challenger's
8
+ conversion credit moves from (engine x rank band) cells to
9
+ (engine x parent-front-region x radius class) cells with the same
10
+ Beta-shrinkage hierarchy, plus a learned demote-only forecast-trust
11
+ channel;
12
+ * R2 (``head_mass_conditional_seat``) — when the calibrated model's
13
+ predicted positive mass concentrates on one candidate strictly above a
14
+ threshold, the FIRST seat becomes the deterministic argmax (an exact
15
+ point-mass, propensity one) instead of a sampled pilot seat; and
16
+ * R3 (``geometry_conditional_elasticity``) — pilot lane selection walks
17
+ D'Hondt over elastic per-lane bids (parent distance-to-front, forecast
18
+ self-overlap saturation, revealed conversion) instead of the fixed
19
+ coverage floor; the within-engine seat design (bands, blocked
20
+ randomization, epsilon floor, exact rational propensities) is delegated
21
+ unchanged to the sequential adaptive pilot.
22
+
23
+ With every flag off, every decision is delegated verbatim to the inner
24
+ ``v8lite_r2`` policy, so the base arm is bit-identical to the reference.
25
+ Terminal seats are ALWAYS delegated through the inner policy, which itself
26
+ delegates to the frozen V7 terminal hierarchical-exploitation rule: no arm
27
+ alters the V7 terminal rule. These arms carry NO live authority; they
28
+ exist for provider-free replay evaluation (gate M-lite v3).
29
+
30
+ The policy knows no workload, objective name, model, provider, or prompt.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import hashlib
36
+ import json
37
+ import re
38
+ from dataclasses import dataclass, field
39
+
40
+ from agent_evolve.application.calibrated_positive_gain_opportunity import (
41
+ ObjectivePoint,
42
+ ObservedConversionOutcome,
43
+ PositiveGainCandidate,
44
+ PositiveGainForecast,
45
+ _require_objective_point,
46
+ )
47
+ from agent_evolve.application.geometry_conditional_elasticity import (
48
+ ElasticSeatBidder,
49
+ ElasticSeatConfig,
50
+ LaneGeometryEvidence,
51
+ )
52
+ from agent_evolve.application.head_mass_conditional_seat import (
53
+ HeadMassSeatAssessor,
54
+ HeadMassSeatConfig,
55
+ )
56
+ from agent_evolve.application.outcome_adaptive_action_racing import (
57
+ AdaptiveActionDescriptor,
58
+ AdaptiveActionOutcome,
59
+ AdaptiveActionSetOutcome,
60
+ )
61
+ from agent_evolve.application.rank_balanced_causal_pilot import (
62
+ PilotSeatObservation,
63
+ RankBalancedPilotCandidate,
64
+ )
65
+ from agent_evolve.application.region_conditional_credit import (
66
+ RegionConditionalChallengerPolicy,
67
+ RegionConditionalOutcome,
68
+ RegionCreditConfig,
69
+ RegionFeatures,
70
+ RegionScoredCandidate,
71
+ parent_front_distance,
72
+ )
73
+ from agent_evolve.application.sequential_market_replay import (
74
+ MarketRecord,
75
+ ReplaySelection,
76
+ ReplayStepReceipt,
77
+ V8LiteReplayPolicy,
78
+ _clamped_gain,
79
+ _corpus_action_sha256,
80
+ _normalized_point,
81
+ )
82
+ from agent_evolve.application.v8lite_allocation_policy import (
83
+ V8LITE_ALLOCATION_POLICY_VERSION_ID_R2,
84
+ V8LITE_PHASE_ADAPTIVE,
85
+ V8LITE_PHASE_PILOT,
86
+ V8LITE_PHASE_PROTECTED_FALLBACK,
87
+ V8LiteAllocationConfig,
88
+ V8LiteAllocationPolicy,
89
+ V8LiteDecision,
90
+ )
91
+ from agent_evolve.domain.patch import require_sha256
92
+ from agent_evolve.domain.typed_json import freeze_json
93
+
94
+ V9_CANDIDATE_POLICY_ID = "v9_candidate_allocation"
95
+ V9_CANDIDATE_POLICY_VERSION = 1
96
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
97
+ _DEFINITION_DOMAIN = b"agent-evolve:v9-candidate-definition:v1\x00"
98
+
99
+
100
+ def _canonical_json(value: object) -> bytes:
101
+ return json.dumps(
102
+ value,
103
+ allow_nan=False,
104
+ ensure_ascii=True,
105
+ separators=(",", ":"),
106
+ sort_keys=True,
107
+ ).encode("ascii", errors="strict")
108
+
109
+
110
+ def _hash(domain: bytes, value: object) -> str:
111
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
112
+
113
+
114
+ def v9_arm_version_id(*, r1: bool, r2: bool, r3: bool) -> str:
115
+ """Deterministic arm token for one flag combination."""
116
+
117
+ suffix = "".join(
118
+ token
119
+ for token, enabled in (("r1", r1), ("r2", r2), ("r3", r3))
120
+ if enabled
121
+ )
122
+ return f"v9_{suffix}" if suffix else "v9_base"
123
+
124
+
125
+ @dataclass(frozen=True, slots=True)
126
+ class V9CandidateConfig:
127
+ """Flags and component configs; the inner v8lite config is shared."""
128
+
129
+ r1_region_conditional_credit: bool = False
130
+ r2_head_mass_conditional_seat: bool = False
131
+ r3_geometry_conditional_elasticity: bool = False
132
+ base: V8LiteAllocationConfig = V8LiteAllocationConfig()
133
+ credit: RegionCreditConfig = RegionCreditConfig()
134
+ head: HeadMassSeatConfig = HeadMassSeatConfig()
135
+ elastic: ElasticSeatConfig = ElasticSeatConfig()
136
+
137
+ def __post_init__(self) -> None:
138
+ for name in (
139
+ "r1_region_conditional_credit",
140
+ "r2_head_mass_conditional_seat",
141
+ "r3_geometry_conditional_elasticity",
142
+ ):
143
+ if type(getattr(self, name)) is not bool:
144
+ raise TypeError(f"{name} must be exact")
145
+ if type(self.base) is not V8LiteAllocationConfig:
146
+ raise TypeError("base must be an exact v8lite config")
147
+ self.base.__post_init__()
148
+ if type(self.credit) is not RegionCreditConfig:
149
+ raise TypeError("credit must be exact")
150
+ self.credit.__post_init__()
151
+ if type(self.head) is not HeadMassSeatConfig:
152
+ raise TypeError("head must be exact")
153
+ self.head.__post_init__()
154
+ if type(self.elastic) is not ElasticSeatConfig:
155
+ raise TypeError("elastic must be exact")
156
+ self.elastic.__post_init__()
157
+
158
+ @property
159
+ def arm_version_id(self) -> str:
160
+ return v9_arm_version_id(
161
+ r1=self.r1_region_conditional_credit,
162
+ r2=self.r2_head_mass_conditional_seat,
163
+ r3=self.r3_geometry_conditional_elasticity,
164
+ )
165
+
166
+ def flags_record(self) -> dict[str, bool]:
167
+ return {
168
+ "r1": self.r1_region_conditional_credit,
169
+ "r2": self.r2_head_mass_conditional_seat,
170
+ "r3": self.r3_geometry_conditional_elasticity,
171
+ }
172
+
173
+
174
+ def _validated_feature_map(
175
+ region_features: tuple[tuple[str, RegionFeatures], ...],
176
+ ) -> dict[str, RegionFeatures]:
177
+ if type(region_features) is not tuple:
178
+ raise TypeError("region_features must be an exact tuple")
179
+ result: dict[str, RegionFeatures] = {}
180
+ for value in region_features:
181
+ if type(value) is not tuple or len(value) != 2:
182
+ raise TypeError(
183
+ "region_features must pair action and features"
184
+ )
185
+ action_sha256, features = value
186
+ require_sha256(action_sha256, "feature action_sha256")
187
+ if type(features) is not RegionFeatures:
188
+ raise TypeError("features must be exact")
189
+ features.__post_init__()
190
+ if action_sha256 in result:
191
+ raise ValueError("region_features repeat an action")
192
+ result[action_sha256] = features
193
+ return result
194
+
195
+
196
+ def _validated_forecast_map(
197
+ forecasts: tuple[tuple[str, PositiveGainForecast], ...],
198
+ ) -> dict[str, PositiveGainForecast]:
199
+ if type(forecasts) is not tuple:
200
+ raise TypeError("forecasts must be an exact tuple")
201
+ result: dict[str, PositiveGainForecast] = {}
202
+ for value in forecasts:
203
+ if type(value) is not tuple or len(value) != 2:
204
+ raise TypeError("forecasts must pair action and forecast")
205
+ action_sha256, forecast = value
206
+ require_sha256(action_sha256, "forecast action_sha256")
207
+ if type(forecast) is not PositiveGainForecast:
208
+ raise TypeError("forecast must be exact")
209
+ forecast.__post_init__()
210
+ if action_sha256 in result:
211
+ raise ValueError("forecasts repeat an action")
212
+ result[action_sha256] = forecast
213
+ return result
214
+
215
+
216
+ def _validated_point_map(
217
+ revealed_objective_points: tuple[tuple[str, ObjectivePoint], ...],
218
+ ) -> dict[str, ObjectivePoint]:
219
+ if type(revealed_objective_points) is not tuple:
220
+ raise TypeError(
221
+ "revealed_objective_points must be an exact tuple"
222
+ )
223
+ result: dict[str, ObjectivePoint] = {}
224
+ for value in revealed_objective_points:
225
+ if type(value) is not tuple or len(value) != 2:
226
+ raise TypeError(
227
+ "revealed_objective_points must pair action and point"
228
+ )
229
+ action_sha256, point = value
230
+ require_sha256(action_sha256, "revealed action_sha256")
231
+ _require_objective_point(point, name="revealed point")
232
+ if action_sha256 in result:
233
+ raise ValueError(
234
+ "revealed_objective_points repeat an action"
235
+ )
236
+ result[action_sha256] = point
237
+ return result
238
+
239
+
240
+ @dataclass(frozen=True, slots=True)
241
+ class V9CandidatePolicy:
242
+ """Compose config-gated R1/R2/R3 over the frozen v8lite_r2 inner."""
243
+
244
+ archive_gain_utility: object = field(repr=False, compare=False)
245
+ config: V9CandidateConfig = V9CandidateConfig()
246
+ policy_id: str = V9_CANDIDATE_POLICY_ID
247
+ policy_version_id: str = field(init=False)
248
+ definition_sha256: str = field(init=False)
249
+
250
+ def __post_init__(self) -> None:
251
+ if type(self.config) is not V9CandidateConfig:
252
+ raise TypeError("config must be an exact v9 config")
253
+ self.config.__post_init__()
254
+ if (
255
+ type(self.policy_id) is not str
256
+ or _TOKEN.fullmatch(self.policy_id) is None
257
+ or self.policy_id != V9_CANDIDATE_POLICY_ID
258
+ ):
259
+ raise ValueError("policy identity is immutable")
260
+ object.__setattr__(
261
+ self,
262
+ "policy_version_id",
263
+ self.config.arm_version_id,
264
+ )
265
+ inner = self.inner_policy()
266
+ challenger = self.region_challenger()
267
+ assessor = self.head_assessor()
268
+ bidder = self.elastic_bidder()
269
+ object.__setattr__(
270
+ self,
271
+ "definition_sha256",
272
+ _hash(
273
+ _DEFINITION_DOMAIN,
274
+ {
275
+ "schema_version": 1,
276
+ "policy_id": self.policy_id,
277
+ "policy_version": V9_CANDIDATE_POLICY_VERSION,
278
+ "policy_version_id": self.policy_version_id,
279
+ "flags": self.config.flags_record(),
280
+ "inner": {
281
+ "policy_id": inner.policy_id,
282
+ "policy_version_id": inner.policy_version_id,
283
+ "definition_sha256": inner.definition_sha256,
284
+ },
285
+ "components": {
286
+ "r1_region_conditional_credit": {
287
+ "policy_id": challenger.policy_id,
288
+ "policy_version": (
289
+ challenger.policy_version
290
+ ),
291
+ "definition_sha256": (
292
+ challenger.definition_sha256
293
+ ),
294
+ },
295
+ "r2_head_mass_conditional_seat": {
296
+ "policy_id": assessor.policy_id,
297
+ "policy_version": assessor.policy_version,
298
+ "definition_sha256": (
299
+ assessor.definition_sha256
300
+ ),
301
+ },
302
+ "r3_geometry_conditional_elasticity": {
303
+ "policy_id": bidder.policy_id,
304
+ "policy_version": bidder.policy_version,
305
+ "definition_sha256": (
306
+ bidder.definition_sha256
307
+ ),
308
+ },
309
+ },
310
+ "base_arm_bit_identical_to_inner": True,
311
+ "v7_terminal_rule_altered": False,
312
+ "live_authority": False,
313
+ "workload_objective_model_provider_prompt_branches": (
314
+ False
315
+ ),
316
+ },
317
+ ),
318
+ )
319
+
320
+ @property
321
+ def r1(self) -> bool:
322
+ return self.config.r1_region_conditional_credit
323
+
324
+ @property
325
+ def r2(self) -> bool:
326
+ return self.config.r2_head_mass_conditional_seat
327
+
328
+ @property
329
+ def r3(self) -> bool:
330
+ return self.config.r3_geometry_conditional_elasticity
331
+
332
+ def inner_policy(self) -> V8LiteAllocationPolicy:
333
+ return V8LiteAllocationPolicy(
334
+ archive_gain_utility=self.archive_gain_utility,
335
+ policy_version_id=(
336
+ V8LITE_ALLOCATION_POLICY_VERSION_ID_R2
337
+ ),
338
+ config=self.config.base,
339
+ )
340
+
341
+ def region_challenger(self) -> RegionConditionalChallengerPolicy:
342
+ return RegionConditionalChallengerPolicy(
343
+ base=self.inner_policy().challenger_policy(),
344
+ credit=self.config.credit,
345
+ )
346
+
347
+ def head_assessor(self) -> HeadMassSeatAssessor:
348
+ return HeadMassSeatAssessor(config=self.config.head)
349
+
350
+ def elastic_bidder(self) -> ElasticSeatBidder:
351
+ return ElasticSeatBidder(
352
+ config=self.config.elastic,
353
+ prior_strength=self.config.base.prior_strength,
354
+ )
355
+
356
+ def identity_record(self) -> dict[str, object]:
357
+ """Public identity dict for one arm combination."""
358
+
359
+ self.__post_init__()
360
+ inner = self.inner_policy()
361
+ challenger = self.region_challenger()
362
+ assessor = self.head_assessor()
363
+ bidder = self.elastic_bidder()
364
+ return {
365
+ "policy_id": self.policy_id,
366
+ "policy_version": V9_CANDIDATE_POLICY_VERSION,
367
+ "policy_version_id": self.policy_version_id,
368
+ "flags": self.config.flags_record(),
369
+ "definition_sha256": self.definition_sha256,
370
+ "inner": inner.identity_record(),
371
+ "components": {
372
+ "r1_region_conditional_credit": {
373
+ "enabled": self.r1,
374
+ "policy_id": challenger.policy_id,
375
+ "definition_sha256": (
376
+ challenger.definition_sha256
377
+ ),
378
+ },
379
+ "r2_head_mass_conditional_seat": {
380
+ "enabled": self.r2,
381
+ "policy_id": assessor.policy_id,
382
+ "definition_sha256": assessor.definition_sha256,
383
+ },
384
+ "r3_geometry_conditional_elasticity": {
385
+ "enabled": self.r3,
386
+ "policy_id": bidder.policy_id,
387
+ "definition_sha256": bidder.definition_sha256,
388
+ },
389
+ },
390
+ "v7_terminal_rule_altered": False,
391
+ "live_authority": False,
392
+ }
393
+
394
+ # ------------------------------------------------------------------
395
+ # Evidence construction shared by R1 scoring and R2 head assessment.
396
+ # ------------------------------------------------------------------
397
+
398
+ def _region_outcomes(
399
+ self,
400
+ *,
401
+ by_action: dict[str, AdaptiveActionDescriptor],
402
+ selected_action_sha256s: tuple[str, ...],
403
+ outcomes: tuple[AdaptiveActionOutcome, ...],
404
+ archive_points: tuple[ObjectivePoint, ...],
405
+ reference_point: ObjectivePoint,
406
+ feature_map: dict[str, RegionFeatures],
407
+ forecast_map: dict[str, PositiveGainForecast],
408
+ point_map: dict[str, ObjectivePoint],
409
+ prior_conversion_outcomes: tuple[
410
+ ObservedConversionOutcome,
411
+ ...,
412
+ ],
413
+ ) -> tuple[RegionConditionalOutcome, ...]:
414
+ """Prior evidence first, then within-market revealed outcomes.
415
+
416
+ Prior (pre-market) evidence carries no comparable parent
417
+ geometry, so it enters the hierarchy at the global and engine
418
+ levels only (``region_id=None``). Within-market outcomes are
419
+ classified against the CURRENT archive; when the outcome's
420
+ candidate carried a forecast, the (predicted, actual) direction
421
+ pair versus the same base archive feeds the trust channel.
422
+ """
423
+
424
+ challenger = self.region_challenger()
425
+ gain_port = self.archive_gain_utility
426
+ result: list[RegionConditionalOutcome] = []
427
+ for ordinal, value in enumerate(
428
+ prior_conversion_outcomes,
429
+ start=1,
430
+ ):
431
+ if type(value) is not ObservedConversionOutcome:
432
+ raise TypeError(
433
+ "prior_conversion_outcomes must contain exact "
434
+ "conversion outcomes"
435
+ )
436
+ result.append(
437
+ RegionConditionalOutcome(
438
+ observation_ordinal=ordinal,
439
+ engine_id=value.engine_id,
440
+ feasible=value.feasible,
441
+ marginal_archive_gain=(
442
+ value.marginal_archive_gain
443
+ ),
444
+ )
445
+ )
446
+ outcome_by_action = {
447
+ value.action_sha256: value for value in outcomes
448
+ }
449
+ for ordinal, action_sha256 in enumerate(
450
+ sorted(selected_action_sha256s),
451
+ start=len(result) + 1,
452
+ ):
453
+ outcome = outcome_by_action[action_sha256]
454
+ descriptor = by_action[action_sha256]
455
+ region_id, radius_class_id = challenger.region_for(
456
+ archive_points=archive_points,
457
+ reference_point=reference_point,
458
+ features=feature_map.get(
459
+ action_sha256,
460
+ RegionFeatures(),
461
+ ),
462
+ )
463
+ forecast = forecast_map.get(action_sha256)
464
+ predicted: bool | None = None
465
+ actual: bool | None = None
466
+ if forecast is not None:
467
+ predicted = (
468
+ gain_port.marginal_archive_gain(
469
+ archive_points,
470
+ forecast.point("p50"),
471
+ )
472
+ > 0.0
473
+ )
474
+ point = point_map.get(action_sha256)
475
+ actual = (
476
+ point is not None
477
+ and gain_port.marginal_archive_gain(
478
+ archive_points,
479
+ point,
480
+ )
481
+ > 0.0
482
+ )
483
+ result.append(
484
+ RegionConditionalOutcome(
485
+ observation_ordinal=ordinal,
486
+ engine_id=descriptor.lane_id,
487
+ feasible=outcome.feasible,
488
+ marginal_archive_gain=(
489
+ outcome.marginal_archive_gain
490
+ ),
491
+ region_id=region_id,
492
+ radius_class_id=radius_class_id,
493
+ forecast_predicted_positive=predicted,
494
+ forecast_actual_positive=actual,
495
+ )
496
+ )
497
+ return tuple(result)
498
+
499
+ def _score_candidates(
500
+ self,
501
+ *,
502
+ candidates: tuple[AdaptiveActionDescriptor, ...],
503
+ archive_points: tuple[ObjectivePoint, ...],
504
+ reference_point: ObjectivePoint,
505
+ feature_map: dict[str, RegionFeatures],
506
+ forecast_map: dict[str, PositiveGainForecast],
507
+ observed_outcomes: tuple[RegionConditionalOutcome, ...],
508
+ future_seats_remaining: int,
509
+ horizon_total: int,
510
+ frozen_fit_training_run_count: int,
511
+ ):
512
+ """Rank candidates with the arm's active calibrated model.
513
+
514
+ With R1 on, the region-conditional challenger scores with full
515
+ region evidence and learned trust. With R1 off, the SAME
516
+ scorer runs with features and leaf evidence stripped, so every
517
+ estimate collapses to the engine/global levels of the shrinkage
518
+ hierarchy and the trust multiplier stays exactly one.
519
+ """
520
+
521
+ challenger = self.region_challenger()
522
+ scored = tuple(
523
+ RegionScoredCandidate(
524
+ candidate=PositiveGainCandidate(
525
+ action_sha256=value.action_sha256,
526
+ engine_id=value.lane_id,
527
+ native_rank=value.native_rank,
528
+ lane_size=value.lane_size,
529
+ forecast=forecast_map.get(value.action_sha256),
530
+ frozen_score=value.prior_score,
531
+ ),
532
+ features=(
533
+ feature_map.get(
534
+ value.action_sha256,
535
+ RegionFeatures(),
536
+ )
537
+ if self.r1
538
+ else RegionFeatures()
539
+ ),
540
+ )
541
+ for value in candidates
542
+ )
543
+ outcomes = (
544
+ observed_outcomes
545
+ if self.r1
546
+ else tuple(
547
+ RegionConditionalOutcome(
548
+ observation_ordinal=value.observation_ordinal,
549
+ engine_id=value.engine_id,
550
+ feasible=value.feasible,
551
+ marginal_archive_gain=(
552
+ value.marginal_archive_gain
553
+ ),
554
+ )
555
+ for value in observed_outcomes
556
+ )
557
+ )
558
+ return challenger.score_market(
559
+ candidates=scored,
560
+ archive_points=archive_points,
561
+ reference_point=reference_point,
562
+ observed_outcomes=outcomes,
563
+ future_seats_remaining=future_seats_remaining,
564
+ horizon_total=horizon_total,
565
+ frozen_fit_training_run_count=(
566
+ frozen_fit_training_run_count
567
+ ),
568
+ )
569
+
570
+ # ------------------------------------------------------------------
571
+ # Pilot seats.
572
+ # ------------------------------------------------------------------
573
+
574
+ def design_pilot_seat(
575
+ self,
576
+ *,
577
+ residual_request_sha256: str,
578
+ actions: tuple[AdaptiveActionDescriptor, ...],
579
+ evaluation_slots: int,
580
+ selected_action_sha256s: tuple[str, ...],
581
+ outcomes: tuple[AdaptiveActionOutcome, ...],
582
+ archive_points: tuple[ObjectivePoint, ...] = (),
583
+ reference_point: ObjectivePoint | None = None,
584
+ region_features: tuple[
585
+ tuple[str, RegionFeatures],
586
+ ...,
587
+ ] = (),
588
+ forecasts: tuple[
589
+ tuple[str, PositiveGainForecast],
590
+ ...,
591
+ ] = (),
592
+ prior_conversion_outcomes: tuple[
593
+ ObservedConversionOutcome,
594
+ ...,
595
+ ] = (),
596
+ frozen_fit_training_run_count: int = 0,
597
+ ) -> V8LiteDecision:
598
+ """One pilot seat under the arm's gated pilot refinements."""
599
+
600
+ self.__post_init__()
601
+ inner = self.inner_policy()
602
+ if not (self.r2 or self.r3):
603
+ return inner.design_pilot_seat(
604
+ residual_request_sha256=residual_request_sha256,
605
+ actions=actions,
606
+ evaluation_slots=evaluation_slots,
607
+ selected_action_sha256s=selected_action_sha256s,
608
+ outcomes=outcomes,
609
+ )
610
+ feature_map = _validated_feature_map(region_features)
611
+ forecast_map = _validated_forecast_map(forecasts)
612
+ by_action = {value.action_sha256: value for value in actions}
613
+ seat_ordinal = len(selected_action_sha256s) + 1
614
+ pilot_width = inner.pilot_width_for(
615
+ evaluation_slots=evaluation_slots,
616
+ engine_count=len(
617
+ {value.lane_id for value in actions}
618
+ ),
619
+ )
620
+ if len(selected_action_sha256s) >= pilot_width:
621
+ raise ValueError("the pilot is already complete")
622
+
623
+ if (
624
+ self.r2
625
+ and seat_ordinal == 1
626
+ and archive_points
627
+ and reference_point is not None
628
+ ):
629
+ ranking = self._score_candidates(
630
+ candidates=actions,
631
+ archive_points=archive_points,
632
+ reference_point=reference_point,
633
+ feature_map=feature_map,
634
+ forecast_map=forecast_map,
635
+ observed_outcomes=self._region_outcomes(
636
+ by_action=by_action,
637
+ selected_action_sha256s=(),
638
+ outcomes=(),
639
+ archive_points=archive_points,
640
+ reference_point=reference_point,
641
+ feature_map=feature_map,
642
+ forecast_map=forecast_map,
643
+ point_map={},
644
+ prior_conversion_outcomes=(
645
+ prior_conversion_outcomes
646
+ ),
647
+ ),
648
+ future_seats_remaining=evaluation_slots - 1,
649
+ horizon_total=evaluation_slots,
650
+ frozen_fit_training_run_count=(
651
+ frozen_fit_training_run_count
652
+ ),
653
+ )
654
+ assessment = self.head_assessor().assess(ranking)
655
+ if assessment.fired:
656
+ return V8LiteDecision(
657
+ policy_id=self.policy_id,
658
+ policy_version_id=self.policy_version_id,
659
+ policy_definition_sha256=self.definition_sha256,
660
+ residual_request_sha256=(
661
+ residual_request_sha256
662
+ ),
663
+ phase=V8LITE_PHASE_PILOT,
664
+ authority_policy_id=(
665
+ self.head_assessor().policy_id
666
+ ),
667
+ selected_action_sha256s=(
668
+ assessment.argmax_action_sha256,
669
+ ),
670
+ selection_propensity=1.0,
671
+ evidence=freeze_json(
672
+ {
673
+ "head_mass_seat": (
674
+ assessment.to_record()
675
+ ),
676
+ "seat_ordinal": 1,
677
+ "deterministic_argmax_seat": True,
678
+ "support_propensities": [
679
+ {
680
+ "action_sha256": (
681
+ assessment.argmax_action_sha256
682
+ ),
683
+ "propensity_hex": (1.0).hex(),
684
+ }
685
+ ],
686
+ "remaining_seats_stochastic": True,
687
+ "candidate_outcomes_observed": False,
688
+ }
689
+ ),
690
+ )
691
+
692
+ if not self.r3:
693
+ return inner.design_pilot_seat(
694
+ residual_request_sha256=residual_request_sha256,
695
+ actions=actions,
696
+ evaluation_slots=evaluation_slots,
697
+ selected_action_sha256s=selected_action_sha256s,
698
+ outcomes=outcomes,
699
+ )
700
+
701
+ # R3: elastic lane bids choose the engine; the within-engine
702
+ # seat is delegated to the inner sequential adaptive pilot over
703
+ # the chosen lane only (band adaptation therefore pools within
704
+ # the lane, which the definition sha records).
705
+ selected = set(selected_action_sha256s)
706
+ outcome_by_action = {
707
+ value.action_sha256: value for value in outcomes
708
+ }
709
+ lanes: dict[str, list[AdaptiveActionDescriptor]] = {}
710
+ for value in actions:
711
+ lanes.setdefault(value.lane_id, []).append(value)
712
+ gain_port = self.archive_gain_utility
713
+ lane_evidence: list[LaneGeometryEvidence] = []
714
+ for engine_id in sorted(lanes):
715
+ members = lanes[engine_id]
716
+ distances: list[float] = []
717
+ predicted: list[bool] = []
718
+ revealed: list[bool] = []
719
+ for value in members:
720
+ features = feature_map.get(value.action_sha256)
721
+ if (
722
+ features is not None
723
+ and features.parent_point is not None
724
+ ):
725
+ distances.append(
726
+ parent_front_distance(
727
+ archive_points,
728
+ features.parent_point,
729
+ )
730
+ )
731
+ forecast = forecast_map.get(value.action_sha256)
732
+ if forecast is not None and archive_points:
733
+ predicted.append(
734
+ gain_port.marginal_archive_gain(
735
+ archive_points,
736
+ forecast.point("p50"),
737
+ )
738
+ <= 0.0
739
+ )
740
+ outcome = outcome_by_action.get(value.action_sha256)
741
+ if outcome is not None:
742
+ revealed.append(
743
+ outcome.marginal_archive_gain > 0.0
744
+ )
745
+ lane_evidence.append(
746
+ LaneGeometryEvidence(
747
+ engine_id=engine_id,
748
+ parent_front_distances=tuple(distances),
749
+ predicted_dominated=tuple(predicted),
750
+ revealed_positive=tuple(revealed),
751
+ )
752
+ )
753
+ bidder = self.elastic_bidder()
754
+ bids = bidder.lane_bids(tuple(lane_evidence))
755
+ seats_awarded = {
756
+ engine_id: sum(
757
+ by_action[value].lane_id == engine_id
758
+ for value in selected_action_sha256s
759
+ )
760
+ for engine_id in lanes
761
+ }
762
+ open_engine_ids = frozenset(
763
+ engine_id
764
+ for engine_id, members in lanes.items()
765
+ if any(
766
+ value.action_sha256 not in selected
767
+ for value in members
768
+ )
769
+ )
770
+ engine_id = bidder.choose_engine(
771
+ bids=bids,
772
+ seats_awarded=seats_awarded,
773
+ open_engine_ids=open_engine_ids,
774
+ )
775
+ lane_members = sorted(
776
+ lanes[engine_id],
777
+ key=lambda value: (
778
+ value.native_rank,
779
+ value.action_sha256,
780
+ ),
781
+ )
782
+ lane_candidates = tuple(
783
+ RankBalancedPilotCandidate(
784
+ action_sha256=value.action_sha256,
785
+ engine_id=value.lane_id,
786
+ native_rank=value.native_rank,
787
+ frozen_score=value.prior_score,
788
+ )
789
+ for value in lane_members
790
+ )
791
+ lane_selected = tuple(
792
+ sorted(
793
+ value
794
+ for value in selected_action_sha256s
795
+ if by_action[value].lane_id == engine_id
796
+ )
797
+ )
798
+ lane_observations = tuple(
799
+ PilotSeatObservation(
800
+ action_sha256=value,
801
+ feasible=outcome_by_action[value].feasible,
802
+ marginal_archive_gain=(
803
+ outcome_by_action[value].marginal_archive_gain
804
+ ),
805
+ )
806
+ for value in lane_selected
807
+ if value in outcome_by_action
808
+ )
809
+ seat = inner.pilot_policy().design_seat(
810
+ residual_request_sha256=residual_request_sha256,
811
+ candidates=lane_candidates,
812
+ selected_action_sha256s=lane_selected,
813
+ observations=lane_observations,
814
+ seat_ordinal=seat_ordinal,
815
+ )
816
+ return V8LiteDecision(
817
+ policy_id=self.policy_id,
818
+ policy_version_id=self.policy_version_id,
819
+ policy_definition_sha256=self.definition_sha256,
820
+ residual_request_sha256=residual_request_sha256,
821
+ phase=V8LITE_PHASE_PILOT,
822
+ authority_policy_id=self.elastic_bidder().policy_id,
823
+ selected_action_sha256s=(
824
+ seat.selected_action_sha256,
825
+ ),
826
+ selection_propensity=seat.selection_propensity,
827
+ evidence=freeze_json(
828
+ {
829
+ "elastic_lane_bids": [
830
+ value.to_record() for value in bids
831
+ ],
832
+ "chosen_engine_id": engine_id,
833
+ "seats_awarded_before": {
834
+ key: value
835
+ for key, value in sorted(
836
+ seats_awarded.items()
837
+ )
838
+ },
839
+ "pilot_seat": seat.to_record(),
840
+ "seat_ordinal": seat_ordinal,
841
+ "fixed_coverage_floor_used": False,
842
+ "candidate_outcomes_observed_before_seat": len(
843
+ outcomes
844
+ ),
845
+ }
846
+ ),
847
+ )
848
+
849
+ # ------------------------------------------------------------------
850
+ # Continuation seats.
851
+ # ------------------------------------------------------------------
852
+
853
+ def select_next(
854
+ self,
855
+ *,
856
+ residual_request_sha256: str,
857
+ actions: tuple[AdaptiveActionDescriptor, ...],
858
+ evaluation_slots: int,
859
+ diagnostic_action_sha256s: tuple[str, ...],
860
+ diagnostic_joint_gain: float,
861
+ selected_action_sha256s: tuple[str, ...],
862
+ outcomes: tuple[AdaptiveActionOutcome, ...],
863
+ archive_points: tuple[ObjectivePoint, ...],
864
+ reference_point: ObjectivePoint | None = None,
865
+ region_features: tuple[
866
+ tuple[str, RegionFeatures],
867
+ ...,
868
+ ] = (),
869
+ forecasts: tuple[
870
+ tuple[str, PositiveGainForecast],
871
+ ...,
872
+ ] = (),
873
+ revealed_objective_points: tuple[
874
+ tuple[str, ObjectivePoint],
875
+ ...,
876
+ ] = (),
877
+ frozen_fit_training_run_count: int = 0,
878
+ prior_conversion_outcomes: tuple[
879
+ ObservedConversionOutcome,
880
+ ...,
881
+ ] = (),
882
+ set_outcomes: tuple[AdaptiveActionSetOutcome, ...] = (),
883
+ ) -> V8LiteDecision:
884
+ """Select one continuation action at the current cutoff."""
885
+
886
+ self.__post_init__()
887
+ inner = self.inner_policy()
888
+ seats_left = evaluation_slots - len(selected_action_sha256s)
889
+ terminal = (
890
+ seats_left <= self.config.base.terminal_hierarchical_slots
891
+ )
892
+ if terminal or not self.r1 or reference_point is None:
893
+ # Terminal seats: EXACT V7 delegation through the inner
894
+ # policy; non-R1 arms: the inner challenger unchanged.
895
+ return inner.select_next(
896
+ residual_request_sha256=residual_request_sha256,
897
+ actions=actions,
898
+ evaluation_slots=evaluation_slots,
899
+ diagnostic_action_sha256s=diagnostic_action_sha256s,
900
+ diagnostic_joint_gain=diagnostic_joint_gain,
901
+ selected_action_sha256s=selected_action_sha256s,
902
+ outcomes=outcomes,
903
+ archive_points=archive_points,
904
+ forecasts=forecasts,
905
+ frozen_fit_training_run_count=(
906
+ frozen_fit_training_run_count
907
+ ),
908
+ prior_conversion_outcomes=(
909
+ prior_conversion_outcomes
910
+ ),
911
+ set_outcomes=set_outcomes,
912
+ )
913
+ feature_map = _validated_feature_map(region_features)
914
+ forecast_map = _validated_forecast_map(forecasts)
915
+ point_map = _validated_point_map(revealed_objective_points)
916
+ by_action = {value.action_sha256: value for value in actions}
917
+ outcome_by_action = {
918
+ value.action_sha256: value for value in outcomes
919
+ }
920
+ if set(outcome_by_action) != set(selected_action_sha256s):
921
+ raise ValueError(
922
+ "observations must exactly cover all previously "
923
+ "selected actions"
924
+ )
925
+ if not set(selected_action_sha256s) <= set(by_action):
926
+ raise ValueError(
927
+ "selected action is outside the sealed market"
928
+ )
929
+ selected_phenotypes = {
930
+ by_action[value].phenotype_sha256
931
+ for value in selected_action_sha256s
932
+ }
933
+ remaining = tuple(
934
+ value
935
+ for value in actions
936
+ if value.action_sha256 not in outcome_by_action
937
+ and value.phenotype_sha256 not in selected_phenotypes
938
+ )
939
+ if not remaining:
940
+ raise ValueError(
941
+ "no unevaluated action can fill the slate"
942
+ )
943
+ observed = self._region_outcomes(
944
+ by_action=by_action,
945
+ selected_action_sha256s=selected_action_sha256s,
946
+ outcomes=outcomes,
947
+ archive_points=archive_points,
948
+ reference_point=reference_point,
949
+ feature_map=feature_map,
950
+ forecast_map=forecast_map,
951
+ point_map=point_map,
952
+ prior_conversion_outcomes=prior_conversion_outcomes,
953
+ )
954
+ ranking = self._score_candidates(
955
+ candidates=remaining,
956
+ archive_points=archive_points,
957
+ reference_point=reference_point,
958
+ feature_map=feature_map,
959
+ forecast_map=forecast_map,
960
+ observed_outcomes=observed,
961
+ future_seats_remaining=seats_left - 1,
962
+ horizon_total=evaluation_slots,
963
+ frozen_fit_training_run_count=(
964
+ frozen_fit_training_run_count
965
+ ),
966
+ )
967
+ top_action_sha256 = ranking.ranked_action_sha256s[0]
968
+ top_score = ranking.score_for(top_action_sha256)
969
+ if top_score.score > 0.0:
970
+ return V8LiteDecision(
971
+ policy_id=self.policy_id,
972
+ policy_version_id=self.policy_version_id,
973
+ policy_definition_sha256=self.definition_sha256,
974
+ residual_request_sha256=residual_request_sha256,
975
+ phase=V8LITE_PHASE_ADAPTIVE,
976
+ authority_policy_id=ranking.policy_id,
977
+ selected_action_sha256s=(top_action_sha256,),
978
+ selection_propensity=1.0,
979
+ evidence=freeze_json(
980
+ {
981
+ "challenger_ranking": ranking.to_record(
982
+ include_scores=True
983
+ ),
984
+ "selected_score_sha256": (
985
+ top_score.score_sha256
986
+ ),
987
+ "protected_fallback_used": False,
988
+ "region_conditional_credit": True,
989
+ "seats_left_before_decision": seats_left,
990
+ "prior_conversion_evidence_count": len(
991
+ prior_conversion_outcomes
992
+ ),
993
+ "unobserved_candidate_outcomes_available": (
994
+ False
995
+ ),
996
+ }
997
+ ),
998
+ challenger_ranking=ranking,
999
+ )
1000
+ # Protected fallback: the frozen V7 incumbent decides, exactly
1001
+ # as the inner v8lite composition falls back.
1002
+ delegated = inner.terminal_policy().select_next(
1003
+ residual_request_sha256=residual_request_sha256,
1004
+ actions=actions,
1005
+ evaluation_slots=evaluation_slots,
1006
+ diagnostic_action_sha256s=diagnostic_action_sha256s,
1007
+ diagnostic_joint_gain=diagnostic_joint_gain,
1008
+ selected_action_sha256s=selected_action_sha256s,
1009
+ outcomes=outcomes,
1010
+ set_outcomes=set_outcomes,
1011
+ )
1012
+ return V8LiteDecision(
1013
+ policy_id=self.policy_id,
1014
+ policy_version_id=self.policy_version_id,
1015
+ policy_definition_sha256=self.definition_sha256,
1016
+ residual_request_sha256=residual_request_sha256,
1017
+ phase=V8LITE_PHASE_PROTECTED_FALLBACK,
1018
+ authority_policy_id=delegated.policy_id,
1019
+ selected_action_sha256s=(
1020
+ delegated.selected_action_sha256s
1021
+ ),
1022
+ selection_propensity=delegated.selection_propensity,
1023
+ evidence=freeze_json(
1024
+ {
1025
+ "protected_fallback_used": True,
1026
+ "fallback_reason": (
1027
+ "challenger_top_score_non_positive"
1028
+ ),
1029
+ "region_conditional_credit": True,
1030
+ "challenger_ranking": ranking.to_record(
1031
+ include_scores=True
1032
+ ),
1033
+ "seats_left_before_decision": seats_left,
1034
+ "delegated_decision": delegated.to_record(
1035
+ include_evidence=True
1036
+ ),
1037
+ }
1038
+ ),
1039
+ delegated_decision=delegated,
1040
+ challenger_ranking=ranking,
1041
+ )
1042
+
1043
+
1044
+ class V9ReplayPolicy:
1045
+ """Drive one V9 arm inside the sealed-market replay boundary.
1046
+
1047
+ The universe, descriptors, and outcome-blind request identity are
1048
+ built by the SAME code the v8lite adapter uses (delegated to an
1049
+ internal ``V8LiteReplayPolicy``), so the base arm is bit-identical
1050
+ to the reference. Region features, forecasts, and revealed
1051
+ objective points are keyed by action and passed through outcome-
1052
+ blind: revealed points cover only already-revealed candidates.
1053
+ """
1054
+
1055
+ def __init__(
1056
+ self,
1057
+ policy: V9CandidatePolicy,
1058
+ *,
1059
+ frozen_fit_training_run_count: int = 0,
1060
+ prior_conversion_outcomes: tuple[
1061
+ ObservedConversionOutcome,
1062
+ ...,
1063
+ ] = (),
1064
+ region_features: tuple[
1065
+ tuple[str, RegionFeatures],
1066
+ ...,
1067
+ ] = (),
1068
+ ) -> None:
1069
+ if type(policy) is not V9CandidatePolicy:
1070
+ raise TypeError("policy must be an exact V9 policy")
1071
+ policy.__post_init__()
1072
+ self.policy_id = (
1073
+ f"replay_adapter.{policy.policy_version_id}"
1074
+ )
1075
+ self._policy = policy
1076
+ self._frozen_fit_training_run_count = (
1077
+ frozen_fit_training_run_count
1078
+ )
1079
+ self._prior_conversion_outcomes = prior_conversion_outcomes
1080
+ self._region_features = _validated_feature_map(
1081
+ region_features
1082
+ )
1083
+ self._helper = V8LiteReplayPolicy(
1084
+ policy.inner_policy(),
1085
+ frozen_fit_training_run_count=(
1086
+ frozen_fit_training_run_count
1087
+ ),
1088
+ prior_conversion_outcomes=prior_conversion_outcomes,
1089
+ )
1090
+
1091
+ def select(
1092
+ self,
1093
+ *,
1094
+ record: MarketRecord,
1095
+ revealed: tuple[ReplayStepReceipt, ...],
1096
+ selectable_action_sha256s: tuple[str, ...],
1097
+ step_index: int,
1098
+ budget: int,
1099
+ ) -> ReplaySelection:
1100
+ universe_ids = tuple(
1101
+ sorted(
1102
+ {
1103
+ *selectable_action_sha256s,
1104
+ *(value.action_sha256 for value in revealed),
1105
+ }
1106
+ )
1107
+ )
1108
+ descriptors = self._helper._descriptors(record, universe_ids)
1109
+ request_sha256 = self._helper._outcome_blind_request_sha256(
1110
+ record,
1111
+ universe_ids,
1112
+ )
1113
+ pilot_width = self._policy.inner_policy().pilot_width_for(
1114
+ evaluation_slots=budget,
1115
+ engine_count=len(
1116
+ {value.lane_id for value in descriptors}
1117
+ ),
1118
+ )
1119
+ if pilot_width <= 0:
1120
+ raise ValueError("replay budget leaves no pilot")
1121
+ selected_ids = tuple(
1122
+ sorted(value.action_sha256 for value in revealed)
1123
+ )
1124
+ outcome_by_action = {
1125
+ value.action_sha256: value for value in revealed
1126
+ }
1127
+ outcomes = tuple(
1128
+ AdaptiveActionOutcome(
1129
+ action_sha256=action_sha256,
1130
+ evaluation_sha256=hashlib.sha256(
1131
+ f"replay-evaluation:{action_sha256}".encode(
1132
+ "ascii"
1133
+ )
1134
+ ).hexdigest(),
1135
+ feasible=outcome_by_action[action_sha256].feasible,
1136
+ marginal_archive_gain=(
1137
+ outcome_by_action[action_sha256].marginal_gain
1138
+ ),
1139
+ )
1140
+ for action_sha256 in selected_ids
1141
+ )
1142
+ forecasts = tuple(
1143
+ (value.action_sha256, value.forecast)
1144
+ for value in (
1145
+ record.candidate(item) for item in universe_ids
1146
+ )
1147
+ if value.forecast is not None
1148
+ )
1149
+ region_features = tuple(
1150
+ sorted(
1151
+ (action_sha256, features)
1152
+ for action_sha256, features in (
1153
+ self._region_features.items()
1154
+ )
1155
+ if action_sha256 in set(universe_ids)
1156
+ )
1157
+ )
1158
+ if step_index < pilot_width:
1159
+ decision = self._policy.design_pilot_seat(
1160
+ residual_request_sha256=request_sha256,
1161
+ actions=descriptors,
1162
+ evaluation_slots=budget,
1163
+ selected_action_sha256s=selected_ids,
1164
+ outcomes=outcomes,
1165
+ archive_points=record.archive_points,
1166
+ reference_point=record.hv_reference_point,
1167
+ region_features=region_features,
1168
+ forecasts=forecasts,
1169
+ prior_conversion_outcomes=(
1170
+ self._prior_conversion_outcomes
1171
+ ),
1172
+ frozen_fit_training_run_count=(
1173
+ self._frozen_fit_training_run_count
1174
+ ),
1175
+ )
1176
+ return ReplaySelection(
1177
+ action_sha256=decision.selected_action_sha256s[0],
1178
+ selection_propensity=decision.selection_propensity,
1179
+ evidence={
1180
+ "phase": decision.phase,
1181
+ "authority_policy_id": (
1182
+ decision.authority_policy_id
1183
+ ),
1184
+ },
1185
+ )
1186
+ pilot_ids = tuple(
1187
+ sorted(
1188
+ value.action_sha256
1189
+ for value in revealed[:pilot_width]
1190
+ )
1191
+ )
1192
+ pilot_points = tuple(
1193
+ record.candidate(value).objectives
1194
+ for value in pilot_ids
1195
+ if record.candidate(value).objectives is not None
1196
+ )
1197
+ diagnostic_joint_gain = _clamped_gain(
1198
+ float(
1199
+ record.hypervolume(pilot_points)
1200
+ - record.hypervolume()
1201
+ )
1202
+ )
1203
+ revealed_objective_points = tuple(
1204
+ (value, record.candidate(value).objectives)
1205
+ for value in selected_ids
1206
+ if record.candidate(value).objectives is not None
1207
+ )
1208
+ decision = self._policy.select_next(
1209
+ residual_request_sha256=request_sha256,
1210
+ actions=descriptors,
1211
+ evaluation_slots=budget,
1212
+ diagnostic_action_sha256s=pilot_ids,
1213
+ diagnostic_joint_gain=diagnostic_joint_gain,
1214
+ selected_action_sha256s=selected_ids,
1215
+ outcomes=outcomes,
1216
+ archive_points=record.archive_points,
1217
+ reference_point=record.hv_reference_point,
1218
+ region_features=region_features,
1219
+ forecasts=forecasts,
1220
+ revealed_objective_points=revealed_objective_points,
1221
+ frozen_fit_training_run_count=(
1222
+ self._frozen_fit_training_run_count
1223
+ ),
1224
+ prior_conversion_outcomes=(
1225
+ self._prior_conversion_outcomes
1226
+ ),
1227
+ )
1228
+ return ReplaySelection(
1229
+ action_sha256=decision.selected_action_sha256s[0],
1230
+ selection_propensity=decision.selection_propensity,
1231
+ evidence={
1232
+ "phase": decision.phase,
1233
+ "authority_policy_id": (
1234
+ decision.authority_policy_id
1235
+ ),
1236
+ },
1237
+ )
1238
+
1239
+
1240
+ def region_features_from_corpus(
1241
+ payload: dict[str, object],
1242
+ ) -> tuple[tuple[str, RegionFeatures], ...]:
1243
+ """Outcome-blind provenance features from one corpus market payload.
1244
+
1245
+ Parent objective points are normalized onto the SAME axes frame the
1246
+ replay loader uses; candidates without a recorded parent objective
1247
+ vector or radius degrade to absent features (region ``no_parent``,
1248
+ radius class ``none``), so markets without provenance stay at the
1249
+ hierarchy's engine level by construction.
1250
+ """
1251
+
1252
+ market_id = str(payload["market_id"])
1253
+ axes = tuple(payload["hv_reference_point"]["axes"])
1254
+ metric_ids = tuple(
1255
+ sorted(str(axis["metric_id"]) for axis in axes)
1256
+ )
1257
+ result: list[tuple[str, RegionFeatures]] = []
1258
+ for raw in payload["candidates"]:
1259
+ action_sha256 = _corpus_action_sha256(market_id, raw)
1260
+ parent = raw.get("parent")
1261
+ parent_point: ObjectivePoint | None = None
1262
+ if isinstance(parent, dict):
1263
+ parent_objectives = parent.get("objectives")
1264
+ if isinstance(parent_objectives, dict) and set(
1265
+ metric_ids
1266
+ ) <= set(parent_objectives):
1267
+ parent_point = _normalized_point(
1268
+ {
1269
+ metric_id: float(
1270
+ parent_objectives[metric_id]
1271
+ )
1272
+ for metric_id in metric_ids
1273
+ },
1274
+ axes,
1275
+ )
1276
+ radius = raw.get("radius")
1277
+ result.append(
1278
+ (
1279
+ action_sha256,
1280
+ RegionFeatures(
1281
+ parent_point=parent_point,
1282
+ radius=(
1283
+ int(radius)
1284
+ if isinstance(radius, int)
1285
+ and not isinstance(radius, bool)
1286
+ and radius >= 0
1287
+ else None
1288
+ ),
1289
+ ),
1290
+ )
1291
+ )
1292
+ return tuple(sorted(result))
1293
+
1294
+
1295
+ __all__ = [
1296
+ "V9_CANDIDATE_POLICY_ID",
1297
+ "V9_CANDIDATE_POLICY_VERSION",
1298
+ "V9CandidateConfig",
1299
+ "V9CandidatePolicy",
1300
+ "V9ReplayPolicy",
1301
+ "region_features_from_corpus",
1302
+ "v9_arm_version_id",
1303
+ ]