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,2762 @@
1
+ """Outcome-safe racing among complete, precommitted portfolio continuations.
2
+
3
+ The module generalizes the binary lineage gate without knowing anything about
4
+ workloads, objectives, models, providers, prompts, or configuration fields.
5
+ Allocation policies first construct several complete slates from one sealed
6
+ proposal market. A small, lane-stratified pilot is then forced into every
7
+ slate before any current outcome exists. Real pilot consequences update only
8
+ the predeclared lane evidence, and a deterministic gate selects one already
9
+ frozen completion.
10
+
11
+ This is deliberately a *portfolio* race rather than candidate-wise Bayesian
12
+ optimization. It preserves exact-K evaluation accounting, avoids
13
+ outcome-conditioned regeneration, and leaves the authoritative evaluator and
14
+ archive utility behind existing injected ports.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import asyncio
20
+ import hashlib
21
+ import json
22
+ import math
23
+ import re
24
+ from dataclasses import dataclass, field
25
+ from typing import Protocol, runtime_checkable
26
+
27
+ from agent_evolve.application.contextual_search_controller import SearchPhase
28
+ from agent_evolve.application.materialized_action_broker import (
29
+ MaterializedActionAllocationRequirement,
30
+ MaterializedActionDescriptor,
31
+ )
32
+ from agent_evolve.application.residual_portfolio_evolution import (
33
+ MaterializedActionAllocationPolicyPort,
34
+ MaterializedActionProposalBatch,
35
+ ResidualPortfolioDecisionRequest,
36
+ )
37
+ from agent_evolve.application.sequential_lineage_allocation import (
38
+ SequentialAllocationGateDecisionPort,
39
+ SequentialAllocationPlanPort,
40
+ SequentialPilotOutcomeBatch,
41
+ )
42
+ from agent_evolve.domain.patch import require_sha256
43
+ from agent_evolve.domain.typed_json import (
44
+ FrozenJsonObject,
45
+ freeze_json,
46
+ thaw_json,
47
+ typed_json_sha256,
48
+ )
49
+
50
+ PRECOMMITTED_PORTFOLIO_RACE_PLANNER_ID = (
51
+ "precommitted_portfolio_race_planner"
52
+ )
53
+ PRECOMMITTED_PORTFOLIO_RACE_PLANNER_VERSION = 4
54
+ EVIDENCE_ADAPTIVE_PORTFOLIO_RACE_GATE_ID = (
55
+ "evidence_adaptive_portfolio_race_gate"
56
+ )
57
+ EVIDENCE_ADAPTIVE_PORTFOLIO_RACE_GATE_VERSION = 2
58
+ TRACE_FIELD_PORTFOLIO_LANE_PROJECTION_ID = (
59
+ "trace_field_portfolio_lane_projection"
60
+ )
61
+ TRACE_FIELD_PORTFOLIO_LANE_PROJECTION_VERSION = 2
62
+ PHASE_CONDITIONED_PORTFOLIO_RACE_PRIOR_ID = (
63
+ "phase_conditioned_portfolio_race_prior"
64
+ )
65
+ PHASE_CONDITIONED_PORTFOLIO_RACE_PRIOR_VERSION = 1
66
+ SYMMETRIC_DIFFERENCE_PORTFOLIO_RACE_POLICY_ID = (
67
+ "symmetric_difference_portfolio_race_policy"
68
+ )
69
+ SYMMETRIC_DIFFERENCE_PORTFOLIO_RACE_POLICY_VERSION = 2
70
+
71
+ _TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
72
+ _BRANCH_DOMAIN = b"agent-evolve:precommitted-race-branch:v1\x00"
73
+ _PLAN_DOMAIN = b"agent-evolve:precommitted-portfolio-race-plan:v1\x00"
74
+ _PLANNER_DEFINITION_DOMAIN = (
75
+ b"agent-evolve:precommitted-portfolio-race-planner-definition:v1\x00"
76
+ )
77
+ _LANE_PROJECTION_DEFINITION_DOMAIN = (
78
+ b"agent-evolve:portfolio-race-lane-projection-definition:v1\x00"
79
+ )
80
+ _PRIOR_PROJECTION_DEFINITION_DOMAIN = (
81
+ b"agent-evolve:portfolio-race-prior-projection-definition:v1\x00"
82
+ )
83
+ _GATE_DEFINITION_DOMAIN = (
84
+ b"agent-evolve:evidence-adaptive-portfolio-race-gate-definition:v1\x00"
85
+ )
86
+ _GATE_DECISION_DOMAIN = (
87
+ b"agent-evolve:evidence-adaptive-portfolio-race-decision:v1\x00"
88
+ )
89
+ _PILOT_REQUIREMENT_DEFINITION_DOMAIN = (
90
+ b"agent-evolve:precommitted-portfolio-race-pilot:v1\x00"
91
+ )
92
+ _BRANCH_REQUIREMENT_DEFINITION_DOMAIN = (
93
+ b"agent-evolve:precommitted-portfolio-race-branch-requirement:v1\x00"
94
+ )
95
+ _DISAGREEMENT_POLICY_DEFINITION_DOMAIN = (
96
+ b"agent-evolve:portfolio-race-disagreement-policy-definition:v1\x00"
97
+ )
98
+ _DISAGREEMENT_DESIGN_DOMAIN = (
99
+ b"agent-evolve:portfolio-race-disagreement-design:v1\x00"
100
+ )
101
+
102
+
103
+ def _canonical_json(value: object) -> bytes:
104
+ return json.dumps(
105
+ value,
106
+ allow_nan=False,
107
+ ensure_ascii=True,
108
+ separators=(",", ":"),
109
+ sort_keys=True,
110
+ ).encode("ascii", errors="strict")
111
+
112
+
113
+ def _hash(domain: bytes, value: object) -> str:
114
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
115
+
116
+
117
+ def _require_token(value: str, *, name: str) -> None:
118
+ if type(value) is not str or _TOKEN.fullmatch(value) is None:
119
+ raise ValueError(f"{name} must use the closed token grammar")
120
+
121
+
122
+ def _policy_identity(
123
+ policy: MaterializedActionAllocationPolicyPort,
124
+ ) -> tuple[str, int, str]:
125
+ if not isinstance(policy, MaterializedActionAllocationPolicyPort):
126
+ raise TypeError("allocation policy must implement its application port")
127
+ identity = (
128
+ getattr(policy, "policy_id", None),
129
+ getattr(policy, "policy_version", None),
130
+ getattr(policy, "definition_sha256", None),
131
+ )
132
+ _require_token(identity[0], name="allocation policy_id")
133
+ if type(identity[1]) is not int or identity[1] <= 0:
134
+ raise ValueError("allocation policy_version must be positive")
135
+ require_sha256(identity[2], "allocation policy definition_sha256")
136
+ return identity # type: ignore[return-value]
137
+
138
+
139
+ def _requirement_trace(
140
+ requirement: MaterializedActionAllocationRequirement,
141
+ ) -> tuple[dict[str, object], ...]:
142
+ requirement.__post_init__()
143
+ evidence = thaw_json(requirement.evidence)
144
+ if type(evidence) is not dict:
145
+ raise TypeError("allocation evidence must have an object root")
146
+ raw_trace = evidence.get("selection_trace")
147
+ if type(raw_trace) is not list:
148
+ raise ValueError("allocation evidence lacks a selection trace")
149
+ rows: list[dict[str, object]] = []
150
+ seen: set[str] = set()
151
+ for fallback_ordinal, raw in enumerate(raw_trace, start=1):
152
+ if type(raw) is not dict:
153
+ raise TypeError("selection trace rows must be objects")
154
+ row = dict(raw)
155
+ action_sha256 = row.get("action_sha256")
156
+ if type(action_sha256) is not str:
157
+ raise TypeError("selection trace omits action_sha256")
158
+ require_sha256(action_sha256, "selection trace action_sha256")
159
+ if action_sha256 in seen:
160
+ raise ValueError("selection trace repeats an action")
161
+ seen.add(action_sha256)
162
+ ordinal = row.get("ordinal", fallback_ordinal)
163
+ if type(ordinal) is not int or ordinal <= 0:
164
+ raise ValueError("selection trace ordinal must be positive")
165
+ row["ordinal"] = ordinal
166
+ rows.append(row)
167
+ required = set(requirement.required_action_sha256s)
168
+ if seen != required:
169
+ raise ValueError(
170
+ "selection trace must exactly cover required action identities"
171
+ )
172
+ return tuple(
173
+ sorted(
174
+ rows,
175
+ key=lambda value: (
176
+ int(value["ordinal"]),
177
+ str(value["action_sha256"]),
178
+ ),
179
+ )
180
+ )
181
+
182
+
183
+ @runtime_checkable
184
+ class PortfolioLaneProjectionPort(Protocol):
185
+ """Project an outcome-blind trace/action pair into a portable lane."""
186
+
187
+ projection_id: str
188
+ projection_version: int
189
+ definition_sha256: str
190
+
191
+ def project(
192
+ self,
193
+ trace_row: dict[str, object],
194
+ action: MaterializedActionDescriptor,
195
+ ) -> str: ...
196
+
197
+
198
+ @dataclass(frozen=True, slots=True)
199
+ class TraceFieldPortfolioLaneProjection:
200
+ """Use portable trace fields, falling back to source/operator identity."""
201
+
202
+ trace_fields: tuple[str, ...] = (
203
+ "score_lane",
204
+ "allocation_kind",
205
+ )
206
+ include_expert_id: bool = True
207
+ include_operator_id: bool = False
208
+ projection_id: str = TRACE_FIELD_PORTFOLIO_LANE_PROJECTION_ID
209
+ projection_version: int = (
210
+ TRACE_FIELD_PORTFOLIO_LANE_PROJECTION_VERSION
211
+ )
212
+ definition_sha256: str = field(init=False)
213
+
214
+ def __post_init__(self) -> None:
215
+ if (
216
+ type(self.trace_fields) is not tuple
217
+ or not self.trace_fields
218
+ or self.trace_fields != tuple(dict.fromkeys(self.trace_fields))
219
+ ):
220
+ raise ValueError(
221
+ "trace_fields must be a non-empty ordered unique tuple"
222
+ )
223
+ for value in self.trace_fields:
224
+ _require_token(value, name="trace field")
225
+ if type(self.include_expert_id) is not bool:
226
+ raise TypeError("include_expert_id must be exact")
227
+ if type(self.include_operator_id) is not bool:
228
+ raise TypeError("include_operator_id must be exact")
229
+ _require_token(self.projection_id, name="projection_id")
230
+ if (
231
+ self.projection_version
232
+ != TRACE_FIELD_PORTFOLIO_LANE_PROJECTION_VERSION
233
+ ):
234
+ raise ValueError("projection_version is immutable")
235
+ object.__setattr__(
236
+ self,
237
+ "definition_sha256",
238
+ _hash(
239
+ _LANE_PROJECTION_DEFINITION_DOMAIN,
240
+ {
241
+ "schema_version": 1,
242
+ "projection_id": self.projection_id,
243
+ "projection_version": self.projection_version,
244
+ "trace_fields": list(self.trace_fields),
245
+ "include_expert_id": self.include_expert_id,
246
+ "include_operator_id": self.include_operator_id,
247
+ "fallback": "allocation_kind_then_action_source",
248
+ "workload_model_provider_branches": False,
249
+ },
250
+ ),
251
+ )
252
+
253
+ def project(
254
+ self,
255
+ trace_row: dict[str, object],
256
+ action: MaterializedActionDescriptor,
257
+ ) -> str:
258
+ self.__post_init__()
259
+ if type(trace_row) is not dict:
260
+ raise TypeError("trace_row must be an exact dict")
261
+ if type(action) is not MaterializedActionDescriptor:
262
+ raise TypeError("action must be exact")
263
+ action.__post_init__()
264
+ parts: list[str] = []
265
+ for field_name in self.trace_fields:
266
+ value = trace_row.get(field_name)
267
+ if type(value) is str and _TOKEN.fullmatch(value) is not None:
268
+ parts.append(f"{field_name}:{value}")
269
+ break
270
+ if not parts:
271
+ parts.append(f"source:{action.expert_id}")
272
+ if self.include_expert_id:
273
+ parts.append(f"expert:{action.expert_id}")
274
+ if self.include_operator_id:
275
+ parts.append(f"operator:{action.operator_id}")
276
+ lane_id = ".".join(parts)
277
+ _require_token(lane_id, name="projected lane_id")
278
+ return lane_id
279
+
280
+
281
+ def _lane_projection_identity(
282
+ value: PortfolioLaneProjectionPort,
283
+ ) -> tuple[str, int, str]:
284
+ if not isinstance(value, PortfolioLaneProjectionPort):
285
+ raise TypeError("lane projection must implement its application port")
286
+ identity = (
287
+ getattr(value, "projection_id", None),
288
+ getattr(value, "projection_version", None),
289
+ getattr(value, "definition_sha256", None),
290
+ )
291
+ _require_token(identity[0], name="lane projection_id")
292
+ if type(identity[1]) is not int or identity[1] <= 0:
293
+ raise ValueError("lane projection_version must be positive")
294
+ require_sha256(identity[2], "lane projection definition_sha256")
295
+ return identity # type: ignore[return-value]
296
+
297
+
298
+ @dataclass(frozen=True, slots=True)
299
+ class PortfolioRacePolicyBinding:
300
+ """One named continuation policy and its outcome-blind prior mean."""
301
+
302
+ branch_id: str
303
+ policy: MaterializedActionAllocationPolicyPort = field(
304
+ repr=False,
305
+ compare=False,
306
+ )
307
+ prior_mean: float = 0.5
308
+
309
+ def __post_init__(self) -> None:
310
+ _require_token(self.branch_id, name="branch_id")
311
+ _policy_identity(self.policy)
312
+ if (
313
+ type(self.prior_mean) is not float
314
+ or not math.isfinite(self.prior_mean)
315
+ or not 0.0 <= self.prior_mean <= 1.0
316
+ ):
317
+ raise ValueError("prior_mean must be finite and lie in [0, 1]")
318
+
319
+ def identity_record(self) -> dict[str, object]:
320
+ identity = _policy_identity(self.policy)
321
+ return {
322
+ "branch_id": self.branch_id,
323
+ "policy_id": identity[0],
324
+ "policy_version": identity[1],
325
+ "policy_definition_sha256": identity[2],
326
+ "prior_mean_hex": self.prior_mean.hex(),
327
+ }
328
+
329
+
330
+ @runtime_checkable
331
+ class PortfolioRacePriorProjectionPort(Protocol):
332
+ """Project frozen search context into one outcome-blind branch prior."""
333
+
334
+ projection_id: str
335
+ projection_version: int
336
+ definition_sha256: str
337
+
338
+ def project(
339
+ self,
340
+ request: ResidualPortfolioDecisionRequest,
341
+ binding: PortfolioRacePolicyBinding,
342
+ source_requirement: MaterializedActionAllocationRequirement,
343
+ ) -> float: ...
344
+
345
+
346
+ def _prior_projection_identity(
347
+ value: PortfolioRacePriorProjectionPort,
348
+ ) -> tuple[str, int, str]:
349
+ if not isinstance(value, PortfolioRacePriorProjectionPort):
350
+ raise TypeError(
351
+ "prior projection must implement its application port"
352
+ )
353
+ identity = (
354
+ getattr(value, "projection_id", None),
355
+ getattr(value, "projection_version", None),
356
+ getattr(value, "definition_sha256", None),
357
+ )
358
+ _require_token(identity[0], name="prior projection_id")
359
+ if type(identity[1]) is not int or identity[1] <= 0:
360
+ raise ValueError("prior projection_version must be positive")
361
+ require_sha256(identity[2], "prior projection definition_sha256")
362
+ return identity # type: ignore[return-value]
363
+
364
+
365
+ @dataclass(frozen=True, slots=True)
366
+ class PhaseConditionedPortfolioRacePrior:
367
+ """Frozen method-branch priors indexed only by generic search phase.
368
+
369
+ The core never knows workload, objective, model, provider, prompt, or
370
+ configuration fields. A training pipeline may fit these bounded means
371
+ from earlier authenticated branch consequences and inject the resulting
372
+ immutable table. Missing cells retain each binding's declared prior.
373
+ """
374
+
375
+ prior_means: tuple[tuple[SearchPhase, str, float], ...]
376
+ projection_id: str = PHASE_CONDITIONED_PORTFOLIO_RACE_PRIOR_ID
377
+ projection_version: int = (
378
+ PHASE_CONDITIONED_PORTFOLIO_RACE_PRIOR_VERSION
379
+ )
380
+ definition_sha256: str = field(init=False)
381
+
382
+ def __post_init__(self) -> None:
383
+ if type(self.prior_means) is not tuple or not self.prior_means:
384
+ raise ValueError("prior_means must be a non-empty exact tuple")
385
+ canonical = tuple(
386
+ sorted(
387
+ self.prior_means,
388
+ key=lambda value: (value[0].value, value[1]),
389
+ )
390
+ )
391
+ if self.prior_means != canonical:
392
+ raise ValueError("phase-conditioned priors must be canonical")
393
+ cells: list[tuple[str, str]] = []
394
+ for phase, branch_id, mean in self.prior_means:
395
+ if type(phase) is not SearchPhase:
396
+ raise TypeError("prior phase must be an exact SearchPhase")
397
+ _require_token(branch_id, name="prior branch_id")
398
+ if (
399
+ type(mean) is not float
400
+ or not math.isfinite(mean)
401
+ or not 0.0 <= mean <= 1.0
402
+ ):
403
+ raise ValueError("branch prior mean must lie in [0, 1]")
404
+ cells.append((phase.value, branch_id))
405
+ if len(cells) != len(set(cells)):
406
+ raise ValueError("phase-conditioned prior cells must be unique")
407
+ _require_token(self.projection_id, name="prior projection_id")
408
+ if (
409
+ self.projection_version
410
+ != PHASE_CONDITIONED_PORTFOLIO_RACE_PRIOR_VERSION
411
+ ):
412
+ raise ValueError("prior projection_version is immutable")
413
+ object.__setattr__(
414
+ self,
415
+ "definition_sha256",
416
+ _hash(
417
+ _PRIOR_PROJECTION_DEFINITION_DOMAIN,
418
+ {
419
+ "schema_version": 1,
420
+ "projection_id": self.projection_id,
421
+ "projection_version": self.projection_version,
422
+ "prior_means": [
423
+ {
424
+ "phase": phase.value,
425
+ "branch_id": branch_id,
426
+ "mean_hex": mean.hex(),
427
+ }
428
+ for phase, branch_id, mean in self.prior_means
429
+ ],
430
+ "missing_cell": "binding_prior_mean",
431
+ "candidate_outcomes_observed": False,
432
+ "workload_model_provider_branches": False,
433
+ },
434
+ ),
435
+ )
436
+
437
+ def project(
438
+ self,
439
+ request: ResidualPortfolioDecisionRequest,
440
+ binding: PortfolioRacePolicyBinding,
441
+ source_requirement: MaterializedActionAllocationRequirement,
442
+ ) -> float:
443
+ self.__post_init__()
444
+ if type(request) is not ResidualPortfolioDecisionRequest:
445
+ raise TypeError("request must be exact")
446
+ request.__post_init__()
447
+ if type(binding) is not PortfolioRacePolicyBinding:
448
+ raise TypeError("binding must be exact")
449
+ binding.__post_init__()
450
+ if (
451
+ type(source_requirement)
452
+ is not MaterializedActionAllocationRequirement
453
+ ):
454
+ raise TypeError("source_requirement must be exact")
455
+ source_requirement.__post_init__()
456
+ if (
457
+ source_requirement.residual_request_sha256
458
+ != request.request_sha256
459
+ ):
460
+ raise ValueError("prior source targets another request")
461
+ matches = tuple(
462
+ mean
463
+ for phase, branch_id, mean in self.prior_means
464
+ if phase is request.phase and branch_id == binding.branch_id
465
+ )
466
+ if len(matches) > 1:
467
+ raise AssertionError("validated prior table repeated a cell")
468
+ return binding.prior_mean if not matches else matches[0]
469
+
470
+ def to_record(self) -> dict[str, object]:
471
+ self.__post_init__()
472
+ return {
473
+ "schema_version": 1,
474
+ "projection_id": self.projection_id,
475
+ "projection_version": self.projection_version,
476
+ "definition_sha256": self.definition_sha256,
477
+ "prior_means": [
478
+ {
479
+ "phase": phase.value,
480
+ "branch_id": branch_id,
481
+ "mean_hex": mean.hex(),
482
+ }
483
+ for phase, branch_id, mean in self.prior_means
484
+ ],
485
+ "missing_cell": "binding_prior_mean",
486
+ "candidate_outcomes_observed": False,
487
+ "workload_model_provider_branches": False,
488
+ }
489
+
490
+
491
+ @dataclass(frozen=True, slots=True)
492
+ class PortfolioRacePilotLaneBinding:
493
+ action_sha256: str
494
+ lane_id: str
495
+
496
+ def __post_init__(self) -> None:
497
+ require_sha256(self.action_sha256, "pilot action_sha256")
498
+ _require_token(self.lane_id, name="pilot lane_id")
499
+
500
+ def to_record(self) -> dict[str, object]:
501
+ self.__post_init__()
502
+ return {
503
+ "action_sha256": self.action_sha256,
504
+ "lane_id": self.lane_id,
505
+ }
506
+
507
+
508
+ @dataclass(frozen=True, slots=True)
509
+ class FrozenPortfolioRaceBranch:
510
+ """One complete slate and its pre-pilot lane exposure."""
511
+
512
+ branch_id: str
513
+ source_requirement_sha256: str
514
+ requirement: MaterializedActionAllocationRequirement
515
+ completion_lane_exposure: tuple[tuple[str, int], ...]
516
+ prior_mean: float
517
+ branch_sha256: str = field(init=False)
518
+
519
+ def __post_init__(self) -> None:
520
+ _require_token(self.branch_id, name="branch_id")
521
+ require_sha256(
522
+ self.source_requirement_sha256,
523
+ "source_requirement_sha256",
524
+ )
525
+ if type(self.requirement) is not MaterializedActionAllocationRequirement:
526
+ raise TypeError("branch requirement must be exact")
527
+ self.requirement.__post_init__()
528
+ if (
529
+ type(self.completion_lane_exposure) is not tuple
530
+ or not self.completion_lane_exposure
531
+ or self.completion_lane_exposure
532
+ != tuple(sorted(self.completion_lane_exposure))
533
+ ):
534
+ raise ValueError(
535
+ "completion_lane_exposure must be non-empty and canonical"
536
+ )
537
+ lane_ids = tuple(value[0] for value in self.completion_lane_exposure)
538
+ if len(lane_ids) != len(set(lane_ids)):
539
+ raise ValueError("completion lane IDs must be unique")
540
+ for lane_id, count in self.completion_lane_exposure:
541
+ _require_token(lane_id, name="completion lane_id")
542
+ if type(count) is not int or count <= 0:
543
+ raise ValueError("completion lane counts must be positive")
544
+ if (
545
+ type(self.prior_mean) is not float
546
+ or not math.isfinite(self.prior_mean)
547
+ or not 0.0 <= self.prior_mean <= 1.0
548
+ ):
549
+ raise ValueError("prior_mean must be finite and lie in [0, 1]")
550
+ object.__setattr__(
551
+ self,
552
+ "branch_sha256",
553
+ _hash(_BRANCH_DOMAIN, self._unsigned_record()),
554
+ )
555
+
556
+ def _unsigned_record(self) -> dict[str, object]:
557
+ return {
558
+ "schema_version": 1,
559
+ "branch_id": self.branch_id,
560
+ "source_requirement_sha256": self.source_requirement_sha256,
561
+ "requirement_sha256": self.requirement.requirement_sha256,
562
+ "completion_lane_exposure": [
563
+ {"lane_id": lane_id, "count": count}
564
+ for lane_id, count in self.completion_lane_exposure
565
+ ],
566
+ "prior_mean_hex": self.prior_mean.hex(),
567
+ }
568
+
569
+ def to_record(self, *, include_evidence: bool = False) -> dict[str, object]:
570
+ self.__post_init__()
571
+ return {
572
+ **self._unsigned_record(),
573
+ "requirement": self.requirement.to_record(
574
+ include_evidence=include_evidence
575
+ ),
576
+ "branch_sha256": self.branch_sha256,
577
+ }
578
+
579
+
580
+ @dataclass(frozen=True, slots=True)
581
+ class PrecommittedPortfolioRacePlan:
582
+ """A shared diagnostic pilot and N complete frozen continuations."""
583
+
584
+ planner_id: str
585
+ planner_version: int
586
+ planner_definition_sha256: str
587
+ residual_request_sha256: str
588
+ proposal_sha256s: tuple[str, ...]
589
+ evaluation_slots: int
590
+ pilot_requirement: MaterializedActionAllocationRequirement
591
+ pilot_lane_bindings: tuple[PortfolioRacePilotLaneBinding, ...]
592
+ branches: tuple[FrozenPortfolioRaceBranch, ...]
593
+ candidate_outcomes_observed: bool
594
+ evidence: FrozenJsonObject
595
+ plan_sha256: str = field(init=False)
596
+
597
+ def __post_init__(self) -> None:
598
+ _require_token(self.planner_id, name="planner_id")
599
+ if type(self.planner_version) is not int or self.planner_version <= 0:
600
+ raise ValueError("planner_version must be positive")
601
+ require_sha256(
602
+ self.planner_definition_sha256,
603
+ "planner_definition_sha256",
604
+ )
605
+ require_sha256(
606
+ self.residual_request_sha256,
607
+ "residual_request_sha256",
608
+ )
609
+ if (
610
+ type(self.proposal_sha256s) is not tuple
611
+ or not self.proposal_sha256s
612
+ or self.proposal_sha256s
613
+ != tuple(sorted(set(self.proposal_sha256s)))
614
+ ):
615
+ raise ValueError("proposal_sha256s must be non-empty and canonical")
616
+ for value in self.proposal_sha256s:
617
+ require_sha256(value, "proposal_sha256")
618
+ if type(self.evaluation_slots) is not int or self.evaluation_slots <= 0:
619
+ raise ValueError("evaluation_slots must be positive")
620
+ if type(self.pilot_requirement) is not MaterializedActionAllocationRequirement:
621
+ raise TypeError("pilot_requirement must be exact")
622
+ self.pilot_requirement.__post_init__()
623
+ if (
624
+ self.pilot_requirement.residual_request_sha256
625
+ != self.residual_request_sha256
626
+ or self.pilot_requirement.proposal_sha256s
627
+ != self.proposal_sha256s
628
+ ):
629
+ raise ValueError("pilot requirement targets another proposal market")
630
+ if (
631
+ type(self.pilot_lane_bindings) is not tuple
632
+ or tuple(
633
+ value.action_sha256 for value in self.pilot_lane_bindings
634
+ )
635
+ != self.pilot_requirement.required_action_sha256s
636
+ ):
637
+ raise ValueError(
638
+ "pilot lane bindings must exactly cover canonical pilot actions"
639
+ )
640
+ for value in self.pilot_lane_bindings:
641
+ if type(value) is not PortfolioRacePilotLaneBinding:
642
+ raise TypeError("pilot lane bindings must be exact")
643
+ value.__post_init__()
644
+ if (
645
+ type(self.branches) is not tuple
646
+ or not self.branches
647
+ or tuple(value.branch_id for value in self.branches)
648
+ != tuple(sorted({value.branch_id for value in self.branches}))
649
+ ):
650
+ raise ValueError(
651
+ "branches must contain canonical unique IDs"
652
+ )
653
+ pilot = set(self.pilot_action_sha256s)
654
+ for branch in self.branches:
655
+ if type(branch) is not FrozenPortfolioRaceBranch:
656
+ raise TypeError("branches must be exact")
657
+ branch.__post_init__()
658
+ requirement = branch.requirement
659
+ if (
660
+ requirement.residual_request_sha256
661
+ != self.residual_request_sha256
662
+ or requirement.proposal_sha256s != self.proposal_sha256s
663
+ or len(requirement.required_action_sha256s)
664
+ != self.evaluation_slots
665
+ or not pilot.issubset(
666
+ requirement.required_action_sha256s
667
+ )
668
+ ):
669
+ raise ValueError(
670
+ "every branch must be a complete pilot-preserving slate"
671
+ )
672
+ if type(self.candidate_outcomes_observed) is not bool:
673
+ raise TypeError("candidate_outcomes_observed must be exact")
674
+ if self.candidate_outcomes_observed:
675
+ raise ValueError("a race plan cannot observe current outcomes")
676
+ if (
677
+ type(self.evidence) is not FrozenJsonObject
678
+ or freeze_json(self.evidence) is not self.evidence
679
+ ):
680
+ raise TypeError("race-plan evidence must be a frozen object")
681
+ object.__setattr__(
682
+ self,
683
+ "plan_sha256",
684
+ _hash(_PLAN_DOMAIN, self._unsigned_record()),
685
+ )
686
+
687
+ @property
688
+ def pilot_action_sha256s(self) -> tuple[str, ...]:
689
+ return self.pilot_requirement.required_action_sha256s
690
+
691
+ @property
692
+ def frozen_branch_ids(self) -> tuple[str, ...]:
693
+ return tuple(value.branch_id for value in self.branches)
694
+
695
+ @property
696
+ def frozen_requirements(
697
+ self,
698
+ ) -> tuple[MaterializedActionAllocationRequirement, ...]:
699
+ return tuple(value.requirement for value in self.branches)
700
+
701
+ def requirement_for(
702
+ self,
703
+ branch_id: str,
704
+ ) -> MaterializedActionAllocationRequirement:
705
+ _require_token(branch_id, name="branch_id")
706
+ matches = tuple(
707
+ value.requirement
708
+ for value in self.branches
709
+ if value.branch_id == branch_id
710
+ )
711
+ if len(matches) != 1:
712
+ raise ValueError("branch_id is not frozen in this plan")
713
+ return matches[0]
714
+
715
+ def branch_for(self, branch_id: str) -> FrozenPortfolioRaceBranch:
716
+ _require_token(branch_id, name="branch_id")
717
+ matches = tuple(
718
+ value for value in self.branches if value.branch_id == branch_id
719
+ )
720
+ if len(matches) != 1:
721
+ raise ValueError("branch_id is not frozen in this plan")
722
+ return matches[0]
723
+
724
+ def _unsigned_record(self) -> dict[str, object]:
725
+ return {
726
+ "schema_version": 1,
727
+ "planner": {
728
+ "planner_id": self.planner_id,
729
+ "planner_version": self.planner_version,
730
+ "definition_sha256": self.planner_definition_sha256,
731
+ },
732
+ "residual_request_sha256": self.residual_request_sha256,
733
+ "proposal_sha256s": list(self.proposal_sha256s),
734
+ "evaluation_slots": self.evaluation_slots,
735
+ "pilot_requirement_sha256": (
736
+ self.pilot_requirement.requirement_sha256
737
+ ),
738
+ "pilot_lane_bindings": [
739
+ value.to_record() for value in self.pilot_lane_bindings
740
+ ],
741
+ "branch_sha256s": [
742
+ value.branch_sha256 for value in self.branches
743
+ ],
744
+ "candidate_outcomes_observed": self.candidate_outcomes_observed,
745
+ "evidence_sha256": typed_json_sha256(self.evidence),
746
+ }
747
+
748
+ def to_record(self, *, include_evidence: bool = False) -> dict[str, object]:
749
+ self.__post_init__()
750
+ record = {
751
+ **self._unsigned_record(),
752
+ "pilot_requirement": self.pilot_requirement.to_record(
753
+ include_evidence=include_evidence
754
+ ),
755
+ "branches": [
756
+ value.to_record(include_evidence=include_evidence)
757
+ for value in self.branches
758
+ ],
759
+ "plan_sha256": self.plan_sha256,
760
+ }
761
+ if include_evidence:
762
+ record["evidence"] = thaw_json(self.evidence)
763
+ return record
764
+
765
+
766
+ def _action_market(
767
+ proposals: tuple[MaterializedActionProposalBatch, ...],
768
+ ) -> dict[str, MaterializedActionDescriptor]:
769
+ market: dict[str, MaterializedActionDescriptor] = {}
770
+ candidate_ids: set[object] = set()
771
+ for proposal in proposals:
772
+ if type(proposal) is not MaterializedActionProposalBatch:
773
+ raise TypeError("proposal market must contain exact batches")
774
+ proposal.__post_init__()
775
+ for action in proposal.actions:
776
+ if action.action_sha256 in market:
777
+ raise ValueError("proposal market repeats an action identity")
778
+ if action.target_candidate_id in candidate_ids:
779
+ raise ValueError("proposal market repeats a target candidate")
780
+ market[action.action_sha256] = action
781
+ candidate_ids.add(action.target_candidate_id)
782
+ return market
783
+
784
+
785
+ def _trace_by_action(
786
+ requirement: MaterializedActionAllocationRequirement,
787
+ ) -> dict[str, dict[str, object]]:
788
+ return {
789
+ str(value["action_sha256"]): value
790
+ for value in _requirement_trace(requirement)
791
+ }
792
+
793
+
794
+ def _ordered_action_ids(
795
+ requirement: MaterializedActionAllocationRequirement,
796
+ ) -> tuple[str, ...]:
797
+ return tuple(
798
+ str(value["action_sha256"])
799
+ for value in _requirement_trace(requirement)
800
+ )
801
+
802
+
803
+ def _stratified_pilot_ids(
804
+ *,
805
+ requirement: MaterializedActionAllocationRequirement,
806
+ market: dict[str, MaterializedActionDescriptor],
807
+ lane_projection: PortfolioLaneProjectionPort,
808
+ pilot_slots: int,
809
+ ) -> tuple[tuple[str, str], ...]:
810
+ trace = _requirement_trace(requirement)
811
+ rows = [
812
+ (
813
+ str(value["action_sha256"]),
814
+ lane_projection.project(
815
+ value,
816
+ market[str(value["action_sha256"])],
817
+ ),
818
+ )
819
+ for value in trace
820
+ ]
821
+ by_lane: dict[str, list[str]] = {}
822
+ for action_sha256, lane_id in rows:
823
+ by_lane.setdefault(lane_id, []).append(action_sha256)
824
+ lane_order = tuple(by_lane)
825
+ selected: list[tuple[str, str]] = []
826
+ selected_actions: set[str] = set()
827
+ selected_phenotypes: set[str] = set()
828
+ depth = 0
829
+ while len(selected) < pilot_slots:
830
+ added = False
831
+ for lane_id in lane_order:
832
+ members = by_lane[lane_id]
833
+ if depth >= len(members):
834
+ continue
835
+ action_sha256 = members[depth]
836
+ action = market[action_sha256]
837
+ if (
838
+ action_sha256 in selected_actions
839
+ or action.phenotype_identity_sha256 in selected_phenotypes
840
+ ):
841
+ continue
842
+ selected.append((action_sha256, lane_id))
843
+ selected_actions.add(action_sha256)
844
+ selected_phenotypes.add(action.phenotype_identity_sha256)
845
+ added = True
846
+ if len(selected) == pilot_slots:
847
+ break
848
+ if not added and all(depth + 1 >= len(by_lane[key]) for key in lane_order):
849
+ break
850
+ depth += 1
851
+ if len(selected) != pilot_slots:
852
+ raise ValueError("pilot policy cannot provide the requested unique slots")
853
+ return tuple(sorted(selected))
854
+
855
+
856
+ def _branch_stratified_pilot_ids(
857
+ *,
858
+ branch_bindings: tuple[PortfolioRacePolicyBinding, ...],
859
+ branch_requirements: tuple[
860
+ MaterializedActionAllocationRequirement, ...
861
+ ],
862
+ market: dict[str, MaterializedActionDescriptor],
863
+ pilot_slots: int,
864
+ ) -> tuple[tuple[str, str], ...]:
865
+ """Select branch-labelled diagnostics before composing shared slates."""
866
+
867
+ if pilot_slots < len(branch_bindings):
868
+ raise ValueError(
869
+ "branch-stratified pilots require at least one slot per branch"
870
+ )
871
+ ordered_by_branch = {
872
+ binding.branch_id: _ordered_action_ids(requirement)
873
+ for binding, requirement in zip(
874
+ branch_bindings,
875
+ branch_requirements,
876
+ strict=True,
877
+ )
878
+ }
879
+ selected: list[tuple[str, str]] = []
880
+ selected_actions: set[str] = set()
881
+ selected_phenotypes: set[str] = set()
882
+ depth = 0
883
+ while len(selected) < pilot_slots:
884
+ added = False
885
+ for binding in branch_bindings:
886
+ candidates = ordered_by_branch[binding.branch_id]
887
+ candidate_index = depth
888
+ while candidate_index < len(candidates):
889
+ action_sha256 = candidates[candidate_index]
890
+ action = market[action_sha256]
891
+ candidate_index += 1
892
+ if (
893
+ action_sha256 in selected_actions
894
+ or action.phenotype_identity_sha256
895
+ in selected_phenotypes
896
+ ):
897
+ continue
898
+ selected.append(
899
+ (action_sha256, f"branch:{binding.branch_id}")
900
+ )
901
+ selected_actions.add(action_sha256)
902
+ selected_phenotypes.add(
903
+ action.phenotype_identity_sha256
904
+ )
905
+ added = True
906
+ break
907
+ if len(selected) == pilot_slots:
908
+ break
909
+ if not added:
910
+ break
911
+ depth += 1
912
+ if len(selected) != pilot_slots:
913
+ raise ValueError(
914
+ "branch policies cannot provide the requested unique pilots"
915
+ )
916
+ return tuple(sorted(selected))
917
+
918
+
919
+ def _project_branch_pilot_lanes(
920
+ *,
921
+ pilot_pairs: tuple[tuple[str, str], ...],
922
+ branch_bindings: tuple[PortfolioRacePolicyBinding, ...],
923
+ branch_requirements: tuple[
924
+ MaterializedActionAllocationRequirement, ...
925
+ ],
926
+ market: dict[str, MaterializedActionDescriptor],
927
+ lane_projection: PortfolioLaneProjectionPort,
928
+ ) -> tuple[
929
+ tuple[tuple[str, str], ...],
930
+ tuple[tuple[str, str, str], ...],
931
+ ]:
932
+ """Replace synthetic branch labels with source-trace exchangeability cells.
933
+
934
+ Branch identity is useful for choosing disagreement pilots, but it is not
935
+ evidence that every later action from that branch is exchangeable with the
936
+ pilot. The returned pilot bindings therefore use the same portable,
937
+ outcome-blind trace projection as continuation actions. A separate
938
+ provenance tuple retains the originating branch without allowing it to
939
+ masquerade as a statistical lane.
940
+ """
941
+
942
+ source_by_branch = {
943
+ binding.branch_id: requirement
944
+ for binding, requirement in zip(
945
+ branch_bindings,
946
+ branch_requirements,
947
+ strict=True,
948
+ )
949
+ }
950
+ trace_by_branch = {
951
+ branch_id: _trace_by_action(requirement)
952
+ for branch_id, requirement in source_by_branch.items()
953
+ }
954
+ projected: list[tuple[str, str]] = []
955
+ provenance: list[tuple[str, str, str]] = []
956
+ for action_sha256, source_lane in pilot_pairs:
957
+ prefix = "branch:"
958
+ if not source_lane.startswith(prefix):
959
+ raise ValueError(
960
+ "branch-stratified pilot lacks source-branch provenance"
961
+ )
962
+ branch_id = source_lane.removeprefix(prefix)
963
+ if branch_id not in source_by_branch:
964
+ raise ValueError("pilot names a branch outside the active race")
965
+ row = trace_by_branch[branch_id].get(action_sha256)
966
+ if row is None:
967
+ raise ValueError(
968
+ "branch-stratified pilot is absent from its source trace"
969
+ )
970
+ lane_id = lane_projection.project(
971
+ row,
972
+ market[action_sha256],
973
+ )
974
+ projected.append((action_sha256, lane_id))
975
+ provenance.append((action_sha256, branch_id, lane_id))
976
+ return (
977
+ tuple(sorted(projected)),
978
+ tuple(sorted(provenance)),
979
+ )
980
+
981
+
982
+ def _compose_branch_action_ids(
983
+ *,
984
+ pilot_action_ids: tuple[str, ...],
985
+ source_requirement: MaterializedActionAllocationRequirement,
986
+ market: dict[str, MaterializedActionDescriptor],
987
+ evaluation_slots: int,
988
+ ) -> tuple[str, ...]:
989
+ selected: list[str] = []
990
+ phenotypes: set[str] = set()
991
+
992
+ def consider(action_sha256: str) -> None:
993
+ if len(selected) >= evaluation_slots or action_sha256 in selected:
994
+ return
995
+ action = market[action_sha256]
996
+ if action.phenotype_identity_sha256 in phenotypes:
997
+ return
998
+ selected.append(action_sha256)
999
+ phenotypes.add(action.phenotype_identity_sha256)
1000
+
1001
+ for value in pilot_action_ids:
1002
+ consider(value)
1003
+ for value in _ordered_action_ids(source_requirement):
1004
+ consider(value)
1005
+ if len(selected) < evaluation_slots:
1006
+ for action in sorted(
1007
+ market.values(),
1008
+ key=lambda value: (
1009
+ value.native_rank,
1010
+ value.expert_id,
1011
+ value.operator_id,
1012
+ value.action_sha256,
1013
+ ),
1014
+ ):
1015
+ consider(action.action_sha256)
1016
+ if len(selected) != evaluation_slots:
1017
+ raise ValueError("proposal market cannot fill a unique-phenotype branch")
1018
+ return tuple(sorted(selected))
1019
+
1020
+
1021
+ @dataclass(frozen=True, slots=True)
1022
+ class PortfolioRaceDisagreementDesign:
1023
+ """Outcome-blind pilot design over behaviorally distinct complete slates."""
1024
+
1025
+ pilot_pairs: tuple[tuple[str, str], ...]
1026
+ branch_action_sha256s: tuple[tuple[str, tuple[str, ...]], ...]
1027
+ evidence: FrozenJsonObject
1028
+ design_sha256: str = field(init=False)
1029
+
1030
+ def __post_init__(self) -> None:
1031
+ if (
1032
+ type(self.pilot_pairs) is not tuple
1033
+ or self.pilot_pairs
1034
+ != tuple(sorted(self.pilot_pairs))
1035
+ ):
1036
+ raise ValueError("pilot_pairs must be canonical")
1037
+ pilot_ids: list[str] = []
1038
+ for action_sha256, lane_id in self.pilot_pairs:
1039
+ require_sha256(action_sha256, "pilot action_sha256")
1040
+ _require_token(lane_id, name="pilot lane_id")
1041
+ pilot_ids.append(action_sha256)
1042
+ if len(pilot_ids) != len(set(pilot_ids)):
1043
+ raise ValueError("pilot_pairs repeat an action")
1044
+ if (
1045
+ type(self.branch_action_sha256s) is not tuple
1046
+ or not self.branch_action_sha256s
1047
+ or self.branch_action_sha256s
1048
+ != tuple(
1049
+ sorted(
1050
+ self.branch_action_sha256s,
1051
+ key=lambda value: value[0],
1052
+ )
1053
+ )
1054
+ ):
1055
+ raise ValueError(
1056
+ "branch_action_sha256s must be non-empty and canonical"
1057
+ )
1058
+ branch_ids: list[str] = []
1059
+ pilot_set = set(pilot_ids)
1060
+ for branch_id, action_sha256s in self.branch_action_sha256s:
1061
+ _require_token(branch_id, name="designed branch_id")
1062
+ branch_ids.append(branch_id)
1063
+ if (
1064
+ type(action_sha256s) is not tuple
1065
+ or not action_sha256s
1066
+ or action_sha256s
1067
+ != tuple(sorted(set(action_sha256s)))
1068
+ ):
1069
+ raise ValueError(
1070
+ "designed branch actions must be non-empty and canonical"
1071
+ )
1072
+ for action_sha256 in action_sha256s:
1073
+ require_sha256(action_sha256, "designed action_sha256")
1074
+ if not pilot_set.issubset(action_sha256s):
1075
+ raise ValueError("every designed branch must retain each pilot")
1076
+ if len(branch_ids) != len(set(branch_ids)):
1077
+ raise ValueError("designed branch IDs must be unique")
1078
+ if (
1079
+ type(self.evidence) is not FrozenJsonObject
1080
+ or freeze_json(self.evidence) is not self.evidence
1081
+ ):
1082
+ raise TypeError("design evidence must be a frozen object")
1083
+ object.__setattr__(
1084
+ self,
1085
+ "design_sha256",
1086
+ _hash(
1087
+ _DISAGREEMENT_DESIGN_DOMAIN,
1088
+ {
1089
+ "schema_version": 1,
1090
+ "pilot_pairs": [
1091
+ {
1092
+ "action_sha256": action_sha256,
1093
+ "lane_id": lane_id,
1094
+ }
1095
+ for action_sha256, lane_id in self.pilot_pairs
1096
+ ],
1097
+ "branch_action_sha256s": [
1098
+ {
1099
+ "branch_id": branch_id,
1100
+ "action_sha256s": list(action_sha256s),
1101
+ }
1102
+ for branch_id, action_sha256s
1103
+ in self.branch_action_sha256s
1104
+ ],
1105
+ "evidence_sha256": typed_json_sha256(self.evidence),
1106
+ },
1107
+ ),
1108
+ )
1109
+
1110
+ def actions_for(self, branch_id: str) -> tuple[str, ...]:
1111
+ _require_token(branch_id, name="branch_id")
1112
+ matches = tuple(
1113
+ action_sha256s
1114
+ for candidate_id, action_sha256s
1115
+ in self.branch_action_sha256s
1116
+ if candidate_id == branch_id
1117
+ )
1118
+ if len(matches) != 1:
1119
+ raise ValueError("branch is absent from disagreement design")
1120
+ return matches[0]
1121
+
1122
+ def to_record(self) -> dict[str, object]:
1123
+ self.__post_init__()
1124
+ return {
1125
+ "schema_version": 1,
1126
+ "pilot_pairs": [
1127
+ {
1128
+ "action_sha256": action_sha256,
1129
+ "lane_id": lane_id,
1130
+ }
1131
+ for action_sha256, lane_id in self.pilot_pairs
1132
+ ],
1133
+ "branch_action_sha256s": [
1134
+ {
1135
+ "branch_id": branch_id,
1136
+ "action_sha256s": list(action_sha256s),
1137
+ }
1138
+ for branch_id, action_sha256s
1139
+ in self.branch_action_sha256s
1140
+ ],
1141
+ "evidence": thaw_json(self.evidence),
1142
+ "design_sha256": self.design_sha256,
1143
+ }
1144
+
1145
+
1146
+ @runtime_checkable
1147
+ class PortfolioRaceDisagreementPolicyPort(Protocol):
1148
+ """Design actionable pilots and distinct slates without current outcomes."""
1149
+
1150
+ policy_id: str
1151
+ policy_version: int
1152
+ definition_sha256: str
1153
+
1154
+ def design(
1155
+ self,
1156
+ *,
1157
+ branch_bindings: tuple[PortfolioRacePolicyBinding, ...],
1158
+ branch_requirements: tuple[
1159
+ MaterializedActionAllocationRequirement,
1160
+ ...,
1161
+ ],
1162
+ market: dict[str, MaterializedActionDescriptor],
1163
+ evaluation_slots: int,
1164
+ maximum_pilot_slots: int,
1165
+ ) -> PortfolioRaceDisagreementDesign: ...
1166
+
1167
+
1168
+ def _disagreement_policy_identity(
1169
+ value: PortfolioRaceDisagreementPolicyPort,
1170
+ ) -> tuple[str, int, str]:
1171
+ if not isinstance(value, PortfolioRaceDisagreementPolicyPort):
1172
+ raise TypeError(
1173
+ "disagreement policy must implement its application port"
1174
+ )
1175
+ identity = (
1176
+ getattr(value, "policy_id", None),
1177
+ getattr(value, "policy_version", None),
1178
+ getattr(value, "definition_sha256", None),
1179
+ )
1180
+ _require_token(identity[0], name="disagreement policy_id")
1181
+ if type(identity[1]) is not int or identity[1] <= 0:
1182
+ raise ValueError("disagreement policy_version must be positive")
1183
+ require_sha256(identity[2], "disagreement policy definition_sha256")
1184
+ return identity # type: ignore[return-value]
1185
+
1186
+
1187
+ def _representative_binding(
1188
+ bindings: tuple[PortfolioRacePolicyBinding, ...],
1189
+ ) -> PortfolioRacePolicyBinding:
1190
+ if not bindings:
1191
+ raise ValueError("an equivalence class needs a representative")
1192
+ return min(
1193
+ bindings,
1194
+ key=lambda value: (-value.prior_mean, value.branch_id),
1195
+ )
1196
+
1197
+
1198
+ def _equivalence_classes(
1199
+ *,
1200
+ bindings: tuple[PortfolioRacePolicyBinding, ...],
1201
+ actions_by_branch: dict[str, tuple[str, ...]],
1202
+ ) -> tuple[
1203
+ tuple[
1204
+ PortfolioRacePolicyBinding,
1205
+ tuple[PortfolioRacePolicyBinding, ...],
1206
+ ],
1207
+ ...,
1208
+ ]:
1209
+ groups: dict[
1210
+ tuple[str, ...],
1211
+ list[PortfolioRacePolicyBinding],
1212
+ ] = {}
1213
+ for binding in bindings:
1214
+ groups.setdefault(
1215
+ actions_by_branch[binding.branch_id],
1216
+ [],
1217
+ ).append(binding)
1218
+ classes = []
1219
+ for members in groups.values():
1220
+ canonical_members = tuple(
1221
+ sorted(members, key=lambda value: value.branch_id)
1222
+ )
1223
+ classes.append(
1224
+ (
1225
+ _representative_binding(canonical_members),
1226
+ canonical_members,
1227
+ )
1228
+ )
1229
+ return tuple(
1230
+ sorted(classes, key=lambda value: value[0].branch_id)
1231
+ )
1232
+
1233
+
1234
+ def _equivalence_class_records(
1235
+ classes: tuple[
1236
+ tuple[
1237
+ PortfolioRacePolicyBinding,
1238
+ tuple[PortfolioRacePolicyBinding, ...],
1239
+ ],
1240
+ ...,
1241
+ ],
1242
+ *,
1243
+ actions_by_branch: dict[str, tuple[str, ...]],
1244
+ ) -> list[dict[str, object]]:
1245
+ return [
1246
+ {
1247
+ "representative_branch_id": representative.branch_id,
1248
+ "member_branch_ids": [
1249
+ value.branch_id for value in members
1250
+ ],
1251
+ "action_sha256s": list(
1252
+ actions_by_branch[representative.branch_id]
1253
+ ),
1254
+ }
1255
+ for representative, members in classes
1256
+ ]
1257
+
1258
+
1259
+ def _outcome_blind_route_resolution(
1260
+ *,
1261
+ source_classes: tuple[
1262
+ tuple[
1263
+ PortfolioRacePolicyBinding,
1264
+ tuple[PortfolioRacePolicyBinding, ...],
1265
+ ],
1266
+ ...,
1267
+ ],
1268
+ final_classes: tuple[
1269
+ tuple[
1270
+ PortfolioRacePolicyBinding,
1271
+ tuple[PortfolioRacePolicyBinding, ...],
1272
+ ],
1273
+ ...,
1274
+ ],
1275
+ ) -> list[dict[str, object]]:
1276
+ """Authenticate source-to-retained aliases through exact slate equality."""
1277
+
1278
+ post_pilot_representative: dict[str, str] = {}
1279
+ for representative, members in final_classes:
1280
+ for member in members:
1281
+ post_pilot_representative[member.branch_id] = (
1282
+ representative.branch_id
1283
+ )
1284
+ rows: list[dict[str, object]] = []
1285
+ for source_representative, source_members in source_classes:
1286
+ retained = post_pilot_representative.get(
1287
+ source_representative.branch_id
1288
+ )
1289
+ if retained is None:
1290
+ continue
1291
+ for member in source_members:
1292
+ path: list[str] = []
1293
+ if member.branch_id != source_representative.branch_id:
1294
+ path.append(
1295
+ "identical_complete_action_set_before_pilot"
1296
+ )
1297
+ if source_representative.branch_id != retained:
1298
+ path.append(
1299
+ "identical_complete_action_set_after_frozen_pilot"
1300
+ )
1301
+ if not path:
1302
+ path.append("identity")
1303
+ rows.append(
1304
+ {
1305
+ "source_branch_id": member.branch_id,
1306
+ "representative_branch_id": retained,
1307
+ "equivalence_path": path,
1308
+ }
1309
+ )
1310
+ return sorted(rows, key=lambda value: str(value["source_branch_id"]))
1311
+
1312
+
1313
+ def _symmetric_difference_pilot_ids(
1314
+ *,
1315
+ branch_bindings: tuple[PortfolioRacePolicyBinding, ...],
1316
+ branch_requirements: dict[
1317
+ str,
1318
+ MaterializedActionAllocationRequirement,
1319
+ ],
1320
+ complete_action_ids: dict[str, tuple[str, ...]],
1321
+ market: dict[str, MaterializedActionDescriptor],
1322
+ pilot_slots: int,
1323
+ ) -> tuple[tuple[str, str], ...]:
1324
+ """Choose rare branch-disagreement actions, never common actions."""
1325
+
1326
+ if type(pilot_slots) is not int or pilot_slots <= 0:
1327
+ raise ValueError("pilot_slots must be positive")
1328
+ action_sets = tuple(
1329
+ set(complete_action_ids[value.branch_id])
1330
+ for value in branch_bindings
1331
+ )
1332
+ common = set.intersection(*action_sets)
1333
+ frequency: dict[str, int] = {}
1334
+ for action_set in action_sets:
1335
+ for action_sha256 in action_set:
1336
+ frequency[action_sha256] = frequency.get(action_sha256, 0) + 1
1337
+ order_by_branch: dict[str, dict[str, int]] = {}
1338
+ for binding in branch_bindings:
1339
+ source_order = _ordered_action_ids(
1340
+ branch_requirements[binding.branch_id]
1341
+ )
1342
+ ordinal = {
1343
+ action_sha256: index
1344
+ for index, action_sha256 in enumerate(source_order)
1345
+ }
1346
+ order_by_branch[binding.branch_id] = ordinal
1347
+ ranked_by_branch: dict[str, tuple[str, ...]] = {}
1348
+ for binding in branch_bindings:
1349
+ branch_id = binding.branch_id
1350
+ ranked_by_branch[branch_id] = tuple(
1351
+ sorted(
1352
+ (
1353
+ action_sha256
1354
+ for action_sha256
1355
+ in complete_action_ids[branch_id]
1356
+ if action_sha256 not in common
1357
+ ),
1358
+ key=lambda action_sha256: (
1359
+ frequency[action_sha256],
1360
+ order_by_branch[branch_id].get(
1361
+ action_sha256,
1362
+ len(market),
1363
+ ),
1364
+ action_sha256,
1365
+ ),
1366
+ )
1367
+ )
1368
+ selection_order = tuple(
1369
+ sorted(
1370
+ branch_bindings,
1371
+ key=lambda value: (-value.prior_mean, value.branch_id),
1372
+ )
1373
+ )
1374
+ selected: list[tuple[str, str]] = []
1375
+ selected_actions: set[str] = set()
1376
+ selected_phenotypes: set[str] = set()
1377
+ depth = 0
1378
+ while len(selected) < pilot_slots:
1379
+ added = False
1380
+ for binding in selection_order:
1381
+ candidates = ranked_by_branch[binding.branch_id]
1382
+ candidate_index = depth
1383
+ while candidate_index < len(candidates):
1384
+ action_sha256 = candidates[candidate_index]
1385
+ candidate_index += 1
1386
+ action = market[action_sha256]
1387
+ if (
1388
+ action_sha256 in selected_actions
1389
+ or action.phenotype_identity_sha256
1390
+ in selected_phenotypes
1391
+ ):
1392
+ continue
1393
+ selected.append(
1394
+ (
1395
+ action_sha256,
1396
+ f"branch:{binding.branch_id}",
1397
+ )
1398
+ )
1399
+ selected_actions.add(action_sha256)
1400
+ selected_phenotypes.add(
1401
+ action.phenotype_identity_sha256
1402
+ )
1403
+ added = True
1404
+ break
1405
+ if len(selected) == pilot_slots:
1406
+ break
1407
+ if not added:
1408
+ break
1409
+ depth += 1
1410
+ if len(selected) != pilot_slots:
1411
+ raise ValueError(
1412
+ "distinct branches cannot provide the requested disagreement pilots"
1413
+ )
1414
+ return tuple(sorted(selected))
1415
+
1416
+
1417
+ @dataclass(frozen=True, slots=True)
1418
+ class SymmetricDifferencePortfolioRacePolicy:
1419
+ """Collapse equivalent slates and race only actionable disagreement.
1420
+
1421
+ ``maximum_pilot_slots`` remains owned by the planner. This policy treats
1422
+ it as a ceiling, searches downward for the largest diagnostic pilot that
1423
+ leaves at least two distinct complete continuations, and emits a zero-pilot
1424
+ deterministic plan when every source slate is behaviorally equivalent.
1425
+ """
1426
+
1427
+ policy_id: str = SYMMETRIC_DIFFERENCE_PORTFOLIO_RACE_POLICY_ID
1428
+ policy_version: int = SYMMETRIC_DIFFERENCE_PORTFOLIO_RACE_POLICY_VERSION
1429
+ definition_sha256: str = field(init=False)
1430
+
1431
+ def __post_init__(self) -> None:
1432
+ _require_token(self.policy_id, name="disagreement policy_id")
1433
+ if (
1434
+ self.policy_version
1435
+ != SYMMETRIC_DIFFERENCE_PORTFOLIO_RACE_POLICY_VERSION
1436
+ ):
1437
+ raise ValueError("disagreement policy_version is immutable")
1438
+ object.__setattr__(
1439
+ self,
1440
+ "definition_sha256",
1441
+ _hash(
1442
+ _DISAGREEMENT_POLICY_DEFINITION_DOMAIN,
1443
+ {
1444
+ "schema_version": 1,
1445
+ "policy_id": self.policy_id,
1446
+ "policy_version": self.policy_version,
1447
+ "source_equivalence": (
1448
+ "identical_complete_action_sha256_set"
1449
+ ),
1450
+ "representative": (
1451
+ "higher_prior_mean_then_branch_id"
1452
+ ),
1453
+ "pilot_support": "source_symmetric_difference_only",
1454
+ "pilot_cardinality": (
1455
+ "largest_at_most_configured_maximum_preserving_"
1456
+ "multiple_complete_continuations"
1457
+ ),
1458
+ "no_actionable_disagreement": (
1459
+ "zero_pilot_deterministic_representative"
1460
+ ),
1461
+ "collapsed_route_resolution": (
1462
+ "authenticated_exact_action_set_equivalence_chain"
1463
+ ),
1464
+ "candidate_outcomes_observed": False,
1465
+ "workload_model_provider_branches": False,
1466
+ },
1467
+ ),
1468
+ )
1469
+
1470
+ def design(
1471
+ self,
1472
+ *,
1473
+ branch_bindings: tuple[PortfolioRacePolicyBinding, ...],
1474
+ branch_requirements: tuple[
1475
+ MaterializedActionAllocationRequirement,
1476
+ ...,
1477
+ ],
1478
+ market: dict[str, MaterializedActionDescriptor],
1479
+ evaluation_slots: int,
1480
+ maximum_pilot_slots: int,
1481
+ ) -> PortfolioRaceDisagreementDesign:
1482
+ self.__post_init__()
1483
+ if (
1484
+ type(branch_bindings) is not tuple
1485
+ or len(branch_bindings) < 2
1486
+ or len(branch_bindings) != len(branch_requirements)
1487
+ ):
1488
+ raise ValueError("disagreement design needs aligned branches")
1489
+ if (
1490
+ type(evaluation_slots) is not int
1491
+ or type(maximum_pilot_slots) is not int
1492
+ or not 0 < maximum_pilot_slots < evaluation_slots
1493
+ ):
1494
+ raise ValueError(
1495
+ "maximum_pilot_slots must leave continuation capacity"
1496
+ )
1497
+ source_by_branch = {
1498
+ binding.branch_id: requirement
1499
+ for binding, requirement in zip(
1500
+ branch_bindings,
1501
+ branch_requirements,
1502
+ strict=True,
1503
+ )
1504
+ }
1505
+ raw_actions = {
1506
+ binding.branch_id: _compose_branch_action_ids(
1507
+ pilot_action_ids=(),
1508
+ source_requirement=source_by_branch[binding.branch_id],
1509
+ market=market,
1510
+ evaluation_slots=evaluation_slots,
1511
+ )
1512
+ for binding in branch_bindings
1513
+ }
1514
+ source_classes = _equivalence_classes(
1515
+ bindings=branch_bindings,
1516
+ actions_by_branch=raw_actions,
1517
+ )
1518
+ active = tuple(value[0] for value in source_classes)
1519
+ source_class_records = _equivalence_class_records(
1520
+ source_classes,
1521
+ actions_by_branch=raw_actions,
1522
+ )
1523
+ attempts: list[dict[str, object]] = []
1524
+ if len(active) == 1:
1525
+ representative = active[0]
1526
+ retained_classes = (
1527
+ (representative, (representative,)),
1528
+ )
1529
+ return PortfolioRaceDisagreementDesign(
1530
+ pilot_pairs=(),
1531
+ branch_action_sha256s=(
1532
+ (
1533
+ representative.branch_id,
1534
+ raw_actions[representative.branch_id],
1535
+ ),
1536
+ ),
1537
+ evidence=freeze_json(
1538
+ {
1539
+ "policy_definition_sha256": self.definition_sha256,
1540
+ "decision": "equivalent_source_bypass",
1541
+ "source_equivalence_classes": source_class_records,
1542
+ "retained_equivalence_classes": (
1543
+ _equivalence_class_records(
1544
+ retained_classes,
1545
+ actions_by_branch=raw_actions,
1546
+ )
1547
+ ),
1548
+ "outcome_blind_route_resolution": (
1549
+ _outcome_blind_route_resolution(
1550
+ source_classes=source_classes,
1551
+ final_classes=retained_classes,
1552
+ )
1553
+ ),
1554
+ "configured_maximum_pilot_slots": (
1555
+ maximum_pilot_slots
1556
+ ),
1557
+ "effective_pilot_slots": 0,
1558
+ "candidate_outcomes_observed": False,
1559
+ "workload_model_provider_branches": False,
1560
+ }
1561
+ ),
1562
+ )
1563
+
1564
+ while len(active) >= 2:
1565
+ largest = min(
1566
+ maximum_pilot_slots,
1567
+ len(active),
1568
+ evaluation_slots - 1,
1569
+ )
1570
+ retained: tuple[PortfolioRacePolicyBinding, ...] | None = None
1571
+ retained_pairs: tuple[tuple[str, str], ...] = ()
1572
+ retained_actions: dict[str, tuple[str, ...]] = {}
1573
+ for requested_slots in range(largest, 0, -1):
1574
+ try:
1575
+ pilot_pairs = _symmetric_difference_pilot_ids(
1576
+ branch_bindings=active,
1577
+ branch_requirements=source_by_branch,
1578
+ complete_action_ids=raw_actions,
1579
+ market=market,
1580
+ pilot_slots=requested_slots,
1581
+ )
1582
+ except ValueError:
1583
+ attempts.append(
1584
+ {
1585
+ "active_branch_ids": [
1586
+ value.branch_id for value in active
1587
+ ],
1588
+ "requested_pilot_slots": requested_slots,
1589
+ "pilot_action_sha256s": [],
1590
+ "distinct_complete_continuations": None,
1591
+ "reason": (
1592
+ "insufficient_unique_disagreement_phenotypes"
1593
+ ),
1594
+ }
1595
+ )
1596
+ continue
1597
+ pilot_ids = tuple(value[0] for value in pilot_pairs)
1598
+ composed = {
1599
+ binding.branch_id: _compose_branch_action_ids(
1600
+ pilot_action_ids=pilot_ids,
1601
+ source_requirement=source_by_branch[
1602
+ binding.branch_id
1603
+ ],
1604
+ market=market,
1605
+ evaluation_slots=evaluation_slots,
1606
+ )
1607
+ for binding in active
1608
+ }
1609
+ final_classes = _equivalence_classes(
1610
+ bindings=active,
1611
+ actions_by_branch=composed,
1612
+ )
1613
+ attempts.append(
1614
+ {
1615
+ "active_branch_ids": [
1616
+ value.branch_id for value in active
1617
+ ],
1618
+ "requested_pilot_slots": requested_slots,
1619
+ "pilot_action_sha256s": list(pilot_ids),
1620
+ "distinct_complete_continuations": len(
1621
+ final_classes
1622
+ ),
1623
+ }
1624
+ )
1625
+ if len(final_classes) >= 2:
1626
+ retained = tuple(
1627
+ value[0] for value in final_classes
1628
+ )
1629
+ retained_pairs = pilot_pairs
1630
+ retained_actions = {
1631
+ representative.branch_id: composed[
1632
+ representative.branch_id
1633
+ ]
1634
+ for representative in retained
1635
+ }
1636
+ break
1637
+ if retained is None:
1638
+ break
1639
+ return PortfolioRaceDisagreementDesign(
1640
+ pilot_pairs=retained_pairs,
1641
+ branch_action_sha256s=tuple(
1642
+ sorted(retained_actions.items())
1643
+ ),
1644
+ evidence=freeze_json(
1645
+ {
1646
+ "policy_definition_sha256": (
1647
+ self.definition_sha256
1648
+ ),
1649
+ "decision": "actionable_disagreement_race",
1650
+ "source_equivalence_classes": (
1651
+ source_class_records
1652
+ ),
1653
+ "retained_equivalence_classes": (
1654
+ _equivalence_class_records(
1655
+ final_classes,
1656
+ actions_by_branch=composed,
1657
+ )
1658
+ ),
1659
+ "outcome_blind_route_resolution": (
1660
+ _outcome_blind_route_resolution(
1661
+ source_classes=source_classes,
1662
+ final_classes=final_classes,
1663
+ )
1664
+ ),
1665
+ "pilot_search_attempts": attempts,
1666
+ "configured_maximum_pilot_slots": (
1667
+ maximum_pilot_slots
1668
+ ),
1669
+ "effective_pilot_slots": len(
1670
+ retained_pairs
1671
+ ),
1672
+ "retained_branch_ids": [
1673
+ value.branch_id for value in retained
1674
+ ],
1675
+ "candidate_outcomes_observed": False,
1676
+ "workload_model_provider_branches": False,
1677
+ }
1678
+ ),
1679
+ )
1680
+
1681
+ representative = _representative_binding(active)
1682
+ representative_source_classes = tuple(
1683
+ value
1684
+ for value in source_classes
1685
+ if value[0].branch_id == representative.branch_id
1686
+ )
1687
+ retained_classes = ((representative, (representative,)),)
1688
+ return PortfolioRaceDisagreementDesign(
1689
+ pilot_pairs=(),
1690
+ branch_action_sha256s=(
1691
+ (
1692
+ representative.branch_id,
1693
+ raw_actions[representative.branch_id],
1694
+ ),
1695
+ ),
1696
+ evidence=freeze_json(
1697
+ {
1698
+ "policy_definition_sha256": self.definition_sha256,
1699
+ "decision": "no_actionable_pilot_bypass",
1700
+ "source_equivalence_classes": source_class_records,
1701
+ "retained_equivalence_classes": (
1702
+ _equivalence_class_records(
1703
+ retained_classes,
1704
+ actions_by_branch=raw_actions,
1705
+ )
1706
+ ),
1707
+ "outcome_blind_route_resolution": (
1708
+ _outcome_blind_route_resolution(
1709
+ source_classes=representative_source_classes,
1710
+ final_classes=retained_classes,
1711
+ )
1712
+ ),
1713
+ "pilot_search_attempts": attempts,
1714
+ "configured_maximum_pilot_slots": maximum_pilot_slots,
1715
+ "effective_pilot_slots": 0,
1716
+ "candidate_outcomes_observed": False,
1717
+ "workload_model_provider_branches": False,
1718
+ }
1719
+ ),
1720
+ )
1721
+
1722
+
1723
+ @dataclass(frozen=True, slots=True)
1724
+ class PrecommittedPortfolioRacePlanner:
1725
+ """Freeze a lane-stratified pilot and N complete portfolio branches."""
1726
+
1727
+ branch_bindings: tuple[PortfolioRacePolicyBinding, ...]
1728
+ pilot_policy: MaterializedActionAllocationPolicyPort | None = field(
1729
+ repr=False,
1730
+ compare=False,
1731
+ )
1732
+ pilot_slots: int
1733
+ disagreement_policy: PortfolioRaceDisagreementPolicyPort | None = (
1734
+ field(default=None, repr=False, compare=False)
1735
+ )
1736
+ prior_projection: PortfolioRacePriorProjectionPort | None = field(
1737
+ default=None,
1738
+ repr=False,
1739
+ compare=False,
1740
+ )
1741
+ lane_projection: PortfolioLaneProjectionPort = field(
1742
+ default_factory=TraceFieldPortfolioLaneProjection,
1743
+ repr=False,
1744
+ compare=False,
1745
+ )
1746
+ planner_id: str = PRECOMMITTED_PORTFOLIO_RACE_PLANNER_ID
1747
+ planner_version: int = PRECOMMITTED_PORTFOLIO_RACE_PLANNER_VERSION
1748
+ definition_sha256: str = field(init=False)
1749
+
1750
+ def __post_init__(self) -> None:
1751
+ if (
1752
+ type(self.branch_bindings) is not tuple
1753
+ or len(self.branch_bindings) < 2
1754
+ ):
1755
+ raise ValueError("a portfolio race needs at least two branches")
1756
+ for value in self.branch_bindings:
1757
+ if type(value) is not PortfolioRacePolicyBinding:
1758
+ raise TypeError("branch bindings must be exact")
1759
+ value.__post_init__()
1760
+ branch_ids = tuple(value.branch_id for value in self.branch_bindings)
1761
+ if branch_ids != tuple(sorted(set(branch_ids))):
1762
+ raise ValueError("branch bindings must use canonical unique IDs")
1763
+ pilot_identity = (
1764
+ None
1765
+ if self.pilot_policy is None
1766
+ else _policy_identity(self.pilot_policy)
1767
+ )
1768
+ disagreement_identity = (
1769
+ None
1770
+ if self.disagreement_policy is None
1771
+ else _disagreement_policy_identity(
1772
+ self.disagreement_policy
1773
+ )
1774
+ )
1775
+ prior_identity = (
1776
+ None
1777
+ if self.prior_projection is None
1778
+ else _prior_projection_identity(self.prior_projection)
1779
+ )
1780
+ if (
1781
+ self.disagreement_policy is not None
1782
+ and self.pilot_policy is not None
1783
+ ):
1784
+ raise ValueError(
1785
+ "disagreement-aware design replaces an injected pilot policy"
1786
+ )
1787
+ if type(self.pilot_slots) is not int or self.pilot_slots <= 0:
1788
+ raise ValueError("pilot_slots must be positive")
1789
+ lane_identity = _lane_projection_identity(self.lane_projection)
1790
+ _require_token(self.planner_id, name="planner_id")
1791
+ if self.planner_version != PRECOMMITTED_PORTFOLIO_RACE_PLANNER_VERSION:
1792
+ raise ValueError("planner_version is immutable")
1793
+ object.__setattr__(
1794
+ self,
1795
+ "definition_sha256",
1796
+ _hash(
1797
+ _PLANNER_DEFINITION_DOMAIN,
1798
+ {
1799
+ "schema_version": 1,
1800
+ "planner_id": self.planner_id,
1801
+ "planner_version": self.planner_version,
1802
+ "branch_policies": [
1803
+ value.identity_record()
1804
+ for value in self.branch_bindings
1805
+ ],
1806
+ "pilot_policy": {
1807
+ "mode": (
1808
+ "branch_stratified"
1809
+ if pilot_identity is None
1810
+ else "injected_policy_lane_stratified"
1811
+ ),
1812
+ "policy_id": (
1813
+ None
1814
+ if pilot_identity is None
1815
+ else pilot_identity[0]
1816
+ ),
1817
+ "policy_version": (
1818
+ None
1819
+ if pilot_identity is None
1820
+ else pilot_identity[1]
1821
+ ),
1822
+ "definition_sha256": (
1823
+ None
1824
+ if pilot_identity is None
1825
+ else pilot_identity[2]
1826
+ ),
1827
+ },
1828
+ "pilot_slots": self.pilot_slots,
1829
+ "disagreement_policy": (
1830
+ None
1831
+ if disagreement_identity is None
1832
+ else {
1833
+ "policy_id": disagreement_identity[0],
1834
+ "policy_version": disagreement_identity[1],
1835
+ "definition_sha256": disagreement_identity[2],
1836
+ }
1837
+ ),
1838
+ "prior_projection": (
1839
+ None
1840
+ if prior_identity is None
1841
+ else {
1842
+ "projection_id": prior_identity[0],
1843
+ "projection_version": prior_identity[1],
1844
+ "definition_sha256": prior_identity[2],
1845
+ }
1846
+ ),
1847
+ "prior_projection_timing": (
1848
+ "before_disagreement_equivalence_resolution"
1849
+ ),
1850
+ "pilot_provenance_projection_scope": (
1851
+ "full_frozen_source_market_before_branch_collapse"
1852
+ ),
1853
+ "lane_projection": {
1854
+ "projection_id": lane_identity[0],
1855
+ "projection_version": lane_identity[1],
1856
+ "definition_sha256": lane_identity[2],
1857
+ },
1858
+ "all_complete_branches_frozen_before_pilot": True,
1859
+ "outcome_conditioned_regeneration": False,
1860
+ "workload_model_provider_branches": False,
1861
+ },
1862
+ ),
1863
+ )
1864
+
1865
+ async def plan(
1866
+ self,
1867
+ request: ResidualPortfolioDecisionRequest,
1868
+ proposals: tuple[MaterializedActionProposalBatch, ...],
1869
+ ) -> SequentialAllocationPlanPort:
1870
+ self.__post_init__()
1871
+ if type(request) is not ResidualPortfolioDecisionRequest:
1872
+ raise TypeError("request must be exact")
1873
+ request.__post_init__()
1874
+ if not 0 < self.pilot_slots < request.evaluation_slots:
1875
+ raise ValueError("pilot_slots must leave a continuation wave")
1876
+ if type(proposals) is not tuple or not proposals:
1877
+ raise ValueError("proposals must be a non-empty exact tuple")
1878
+ for proposal in proposals:
1879
+ if type(proposal) is not MaterializedActionProposalBatch:
1880
+ raise TypeError("proposals must contain exact batches")
1881
+ proposal.__post_init__()
1882
+ proposal.require_request(request)
1883
+ if self.pilot_policy is None:
1884
+ branch_sources = tuple(
1885
+ await asyncio.gather(
1886
+ *(
1887
+ value.policy.require(request, proposals)
1888
+ for value in self.branch_bindings
1889
+ )
1890
+ )
1891
+ )
1892
+ requirements = branch_sources
1893
+ pilot_source = None
1894
+ else:
1895
+ requirements = tuple(
1896
+ await asyncio.gather(
1897
+ self.pilot_policy.require(request, proposals),
1898
+ *(
1899
+ value.policy.require(request, proposals)
1900
+ for value in self.branch_bindings
1901
+ ),
1902
+ )
1903
+ )
1904
+ pilot_source = requirements[0]
1905
+ branch_sources = requirements[1:]
1906
+ for requirement in requirements:
1907
+ if type(requirement) is not MaterializedActionAllocationRequirement:
1908
+ raise TypeError("allocation policy returned a foreign requirement")
1909
+ requirement.__post_init__()
1910
+ if (
1911
+ requirement.residual_request_sha256
1912
+ != request.request_sha256
1913
+ ):
1914
+ raise ValueError("allocation policy targeted another request")
1915
+ proposal_sha256s = tuple(
1916
+ sorted(value.proposal_sha256 for value in proposals)
1917
+ )
1918
+ if any(
1919
+ value.proposal_sha256s != proposal_sha256s
1920
+ for value in requirements
1921
+ ):
1922
+ raise ValueError("allocation policies saw different proposal markets")
1923
+ market = _action_market(proposals)
1924
+ source_by_branch = {
1925
+ binding.branch_id: source
1926
+ for binding, source in zip(
1927
+ self.branch_bindings,
1928
+ branch_sources,
1929
+ strict=True,
1930
+ )
1931
+ }
1932
+ projected_prior_by_branch = {
1933
+ binding.branch_id: (
1934
+ binding.prior_mean
1935
+ if self.prior_projection is None
1936
+ else self.prior_projection.project(
1937
+ request,
1938
+ binding,
1939
+ source_by_branch[binding.branch_id],
1940
+ )
1941
+ )
1942
+ for binding in self.branch_bindings
1943
+ }
1944
+ effective_branch_bindings = tuple(
1945
+ PortfolioRacePolicyBinding(
1946
+ branch_id=binding.branch_id,
1947
+ policy=binding.policy,
1948
+ prior_mean=projected_prior_by_branch[binding.branch_id],
1949
+ )
1950
+ for binding in self.branch_bindings
1951
+ )
1952
+ disagreement_design = (
1953
+ None
1954
+ if self.disagreement_policy is None
1955
+ else self.disagreement_policy.design(
1956
+ branch_bindings=effective_branch_bindings,
1957
+ branch_requirements=branch_sources,
1958
+ market=market,
1959
+ evaluation_slots=request.evaluation_slots,
1960
+ maximum_pilot_slots=self.pilot_slots,
1961
+ )
1962
+ )
1963
+ if (
1964
+ disagreement_design is not None
1965
+ and type(disagreement_design)
1966
+ is not PortfolioRaceDisagreementDesign
1967
+ ):
1968
+ raise TypeError(
1969
+ "disagreement policy returned a foreign design"
1970
+ )
1971
+ if disagreement_design is not None:
1972
+ disagreement_design.__post_init__()
1973
+ if disagreement_design is None:
1974
+ active_bindings = effective_branch_bindings
1975
+ active_sources = branch_sources
1976
+ raw_pilot_pairs = (
1977
+ _branch_stratified_pilot_ids(
1978
+ branch_bindings=active_bindings,
1979
+ branch_requirements=active_sources,
1980
+ market=market,
1981
+ pilot_slots=self.pilot_slots,
1982
+ )
1983
+ if pilot_source is None
1984
+ else _stratified_pilot_ids(
1985
+ requirement=pilot_source,
1986
+ market=market,
1987
+ lane_projection=self.lane_projection,
1988
+ pilot_slots=self.pilot_slots,
1989
+ )
1990
+ )
1991
+ else:
1992
+ designed_branch_ids = {
1993
+ value[0]
1994
+ for value in disagreement_design.branch_action_sha256s
1995
+ }
1996
+ active_bindings = tuple(
1997
+ value
1998
+ for value in effective_branch_bindings
1999
+ if value.branch_id in designed_branch_ids
2000
+ )
2001
+ active_sources = tuple(
2002
+ source_by_branch[value.branch_id]
2003
+ for value in active_bindings
2004
+ )
2005
+ raw_pilot_pairs = disagreement_design.pilot_pairs
2006
+ if pilot_source is None:
2007
+ projection_bindings = (
2008
+ effective_branch_bindings
2009
+ if disagreement_design is not None
2010
+ else active_bindings
2011
+ )
2012
+ projection_sources = (
2013
+ branch_sources
2014
+ if disagreement_design is not None
2015
+ else active_sources
2016
+ )
2017
+ pilot_pairs, pilot_source_attribution = (
2018
+ _project_branch_pilot_lanes(
2019
+ pilot_pairs=raw_pilot_pairs,
2020
+ branch_bindings=projection_bindings,
2021
+ branch_requirements=projection_sources,
2022
+ market=market,
2023
+ lane_projection=self.lane_projection,
2024
+ )
2025
+ )
2026
+ else:
2027
+ pilot_pairs = raw_pilot_pairs
2028
+ pilot_source_attribution = tuple(
2029
+ (
2030
+ action_sha256,
2031
+ "injected_pilot_policy",
2032
+ lane_id,
2033
+ )
2034
+ for action_sha256, lane_id in pilot_pairs
2035
+ )
2036
+ prior_by_branch = {
2037
+ binding.branch_id: binding.prior_mean
2038
+ for binding in active_bindings
2039
+ }
2040
+ pilot_ids = tuple(value[0] for value in pilot_pairs)
2041
+ pilot_source_sha256s = (
2042
+ tuple(
2043
+ value.requirement_sha256 for value in branch_sources
2044
+ )
2045
+ if pilot_source is None
2046
+ else (pilot_source.requirement_sha256,)
2047
+ )
2048
+ pilot_definition_sha256 = _hash(
2049
+ _PILOT_REQUIREMENT_DEFINITION_DOMAIN,
2050
+ {
2051
+ "planner_definition_sha256": self.definition_sha256,
2052
+ "source_requirement_sha256s": list(
2053
+ pilot_source_sha256s
2054
+ ),
2055
+ "mode": (
2056
+ "symmetric_difference_adaptive"
2057
+ if disagreement_design is not None
2058
+ else (
2059
+ "branch_stratified"
2060
+ if pilot_source is None
2061
+ else "injected_policy_lane_stratified"
2062
+ )
2063
+ ),
2064
+ "configured_maximum_pilot_slots": self.pilot_slots,
2065
+ "effective_pilot_slots": len(pilot_pairs),
2066
+ "pilot_lane_bindings": [
2067
+ {"action_sha256": action, "lane_id": lane}
2068
+ for action, lane in pilot_pairs
2069
+ ],
2070
+ },
2071
+ )
2072
+ pilot_requirement = MaterializedActionAllocationRequirement(
2073
+ policy_id="precommitted_portfolio_race_pilot",
2074
+ policy_version=1,
2075
+ policy_definition_sha256=pilot_definition_sha256,
2076
+ residual_request_sha256=request.request_sha256,
2077
+ proposal_sha256s=proposal_sha256s,
2078
+ required_action_sha256s=pilot_ids,
2079
+ candidate_outcomes_observed=False,
2080
+ evidence=freeze_json(
2081
+ {
2082
+ "selection_trace": [
2083
+ {
2084
+ "ordinal": ordinal,
2085
+ "action_sha256": action_sha256,
2086
+ "allocation_kind": "portfolio_race_pilot",
2087
+ "source_lane": lane_id,
2088
+ "source_branch_id": next(
2089
+ source_branch_id
2090
+ for (
2091
+ source_action_sha256,
2092
+ source_branch_id,
2093
+ _projected_lane_id,
2094
+ ) in pilot_source_attribution
2095
+ if source_action_sha256 == action_sha256
2096
+ ),
2097
+ "candidate_outcomes_observed": False,
2098
+ }
2099
+ for ordinal, (action_sha256, lane_id) in enumerate(
2100
+ pilot_pairs,
2101
+ start=1,
2102
+ )
2103
+ ],
2104
+ "source_requirement_sha256s": list(
2105
+ pilot_source_sha256s
2106
+ ),
2107
+ "mode": (
2108
+ "symmetric_difference_adaptive"
2109
+ if disagreement_design is not None
2110
+ else (
2111
+ "branch_stratified"
2112
+ if pilot_source is None
2113
+ else "injected_policy_lane_stratified"
2114
+ )
2115
+ ),
2116
+ "lane_stratified": bool(pilot_pairs),
2117
+ "configured_maximum_pilot_slots": (
2118
+ self.pilot_slots
2119
+ ),
2120
+ "effective_pilot_slots": len(pilot_pairs),
2121
+ "disagreement_design_sha256": (
2122
+ None
2123
+ if disagreement_design is None
2124
+ else disagreement_design.design_sha256
2125
+ ),
2126
+ "pilot_source_attribution": [
2127
+ {
2128
+ "action_sha256": action_sha256,
2129
+ "source_branch_id": source_branch_id,
2130
+ "projected_lane_id": lane_id,
2131
+ }
2132
+ for (
2133
+ action_sha256,
2134
+ source_branch_id,
2135
+ lane_id,
2136
+ ) in pilot_source_attribution
2137
+ ],
2138
+ "branch_identity_used_as_exchangeability_lane": False,
2139
+ "candidate_outcomes_observed": False,
2140
+ "workload_model_provider_branches": False,
2141
+ }
2142
+ ),
2143
+ )
2144
+
2145
+ branches: list[FrozenPortfolioRaceBranch] = []
2146
+ for binding, source in zip(
2147
+ active_bindings,
2148
+ active_sources,
2149
+ strict=True,
2150
+ ):
2151
+ source_trace = _trace_by_action(source)
2152
+ final_ids = (
2153
+ _compose_branch_action_ids(
2154
+ pilot_action_ids=pilot_ids,
2155
+ source_requirement=source,
2156
+ market=market,
2157
+ evaluation_slots=request.evaluation_slots,
2158
+ )
2159
+ if disagreement_design is None
2160
+ else disagreement_design.actions_for(
2161
+ binding.branch_id
2162
+ )
2163
+ )
2164
+ completion_ids = tuple(
2165
+ value for value in final_ids if value not in set(pilot_ids)
2166
+ )
2167
+ lane_by_action: dict[str, str] = {}
2168
+ for action_sha256 in completion_ids:
2169
+ row = source_trace.get(
2170
+ action_sha256,
2171
+ {
2172
+ "action_sha256": action_sha256,
2173
+ "allocation_kind": "deterministic_market_refill",
2174
+ },
2175
+ )
2176
+ lane_by_action[action_sha256] = (
2177
+ self.lane_projection.project(
2178
+ row,
2179
+ market[action_sha256],
2180
+ )
2181
+ )
2182
+ exposure: dict[str, int] = {}
2183
+ for lane_id in lane_by_action.values():
2184
+ exposure[lane_id] = exposure.get(lane_id, 0) + 1
2185
+ branch_definition_sha256 = _hash(
2186
+ _BRANCH_REQUIREMENT_DEFINITION_DOMAIN,
2187
+ {
2188
+ "planner_definition_sha256": self.definition_sha256,
2189
+ "branch_id": binding.branch_id,
2190
+ "source_requirement_sha256": (
2191
+ source.requirement_sha256
2192
+ ),
2193
+ "pilot_requirement_sha256": (
2194
+ pilot_requirement.requirement_sha256
2195
+ ),
2196
+ "completion_lane_exposure": [
2197
+ {"lane_id": lane_id, "count": count}
2198
+ for lane_id, count in sorted(exposure.items())
2199
+ ],
2200
+ },
2201
+ )
2202
+ branch_requirement = MaterializedActionAllocationRequirement(
2203
+ policy_id="precommitted_portfolio_race_branch",
2204
+ policy_version=1,
2205
+ policy_definition_sha256=branch_definition_sha256,
2206
+ residual_request_sha256=request.request_sha256,
2207
+ proposal_sha256s=proposal_sha256s,
2208
+ required_action_sha256s=final_ids,
2209
+ candidate_outcomes_observed=False,
2210
+ evidence=freeze_json(
2211
+ {
2212
+ "branch_id": binding.branch_id,
2213
+ "source_requirement_sha256": (
2214
+ source.requirement_sha256
2215
+ ),
2216
+ "selection_trace": [
2217
+ {
2218
+ "ordinal": ordinal,
2219
+ "action_sha256": action_sha256,
2220
+ "allocation_kind": (
2221
+ "portfolio_race_common_pilot"
2222
+ if action_sha256 in set(pilot_ids)
2223
+ else "portfolio_race_completion"
2224
+ ),
2225
+ "source_lane": (
2226
+ next(
2227
+ lane
2228
+ for action, lane in pilot_pairs
2229
+ if action == action_sha256
2230
+ )
2231
+ if action_sha256 in set(pilot_ids)
2232
+ else lane_by_action[action_sha256]
2233
+ ),
2234
+ "candidate_outcomes_observed": False,
2235
+ }
2236
+ for ordinal, action_sha256 in enumerate(
2237
+ final_ids,
2238
+ start=1,
2239
+ )
2240
+ ],
2241
+ "completion_lane_exposure": [
2242
+ {"lane_id": lane_id, "count": count}
2243
+ for lane_id, count in sorted(exposure.items())
2244
+ ],
2245
+ "candidate_outcomes_observed": False,
2246
+ "workload_model_provider_branches": False,
2247
+ }
2248
+ ),
2249
+ )
2250
+ branches.append(
2251
+ FrozenPortfolioRaceBranch(
2252
+ branch_id=binding.branch_id,
2253
+ source_requirement_sha256=source.requirement_sha256,
2254
+ requirement=branch_requirement,
2255
+ completion_lane_exposure=tuple(sorted(exposure.items())),
2256
+ prior_mean=prior_by_branch[binding.branch_id],
2257
+ )
2258
+ )
2259
+ return PrecommittedPortfolioRacePlan(
2260
+ planner_id=self.planner_id,
2261
+ planner_version=self.planner_version,
2262
+ planner_definition_sha256=self.definition_sha256,
2263
+ residual_request_sha256=request.request_sha256,
2264
+ proposal_sha256s=proposal_sha256s,
2265
+ evaluation_slots=request.evaluation_slots,
2266
+ pilot_requirement=pilot_requirement,
2267
+ pilot_lane_bindings=tuple(
2268
+ PortfolioRacePilotLaneBinding(
2269
+ action_sha256=action_sha256,
2270
+ lane_id=lane_id,
2271
+ )
2272
+ for action_sha256, lane_id in pilot_pairs
2273
+ ),
2274
+ branches=tuple(sorted(branches, key=lambda value: value.branch_id)),
2275
+ candidate_outcomes_observed=False,
2276
+ evidence=freeze_json(
2277
+ {
2278
+ "pilot_source_requirement_sha256s": list(
2279
+ pilot_source_sha256s
2280
+ ),
2281
+ "pilot_mode": (
2282
+ "symmetric_difference_adaptive"
2283
+ if disagreement_design is not None
2284
+ else (
2285
+ "branch_stratified"
2286
+ if pilot_source is None
2287
+ else "injected_policy_lane_stratified"
2288
+ )
2289
+ ),
2290
+ "all_branch_source_requirement_sha256s": [
2291
+ value.source_requirement_sha256
2292
+ for value in sorted(
2293
+ branches,
2294
+ key=lambda item: item.branch_id,
2295
+ )
2296
+ ],
2297
+ "complete_branch_count": len(branches),
2298
+ "configured_maximum_pilot_slots": self.pilot_slots,
2299
+ "effective_pilot_slots": len(pilot_pairs),
2300
+ "pilot_source_attribution": [
2301
+ {
2302
+ "action_sha256": action_sha256,
2303
+ "source_branch_id": source_branch_id,
2304
+ "projected_lane_id": lane_id,
2305
+ }
2306
+ for (
2307
+ action_sha256,
2308
+ source_branch_id,
2309
+ lane_id,
2310
+ ) in pilot_source_attribution
2311
+ ],
2312
+ "pilot_and_completion_lanes_share_projection": True,
2313
+ "branch_identity_used_as_exchangeability_lane": False,
2314
+ "branch_prior_projection": (
2315
+ {
2316
+ "mode": "binding_prior_mean",
2317
+ "prior_means": [
2318
+ {
2319
+ "branch_id": branch_id,
2320
+ "mean_hex": mean.hex(),
2321
+ }
2322
+ for branch_id, mean in sorted(
2323
+ projected_prior_by_branch.items()
2324
+ )
2325
+ ],
2326
+ "applied_before_disagreement_equivalence": True,
2327
+ "candidate_outcomes_observed": False,
2328
+ }
2329
+ if self.prior_projection is None
2330
+ else {
2331
+ "mode": "injected_outcome_blind_projection",
2332
+ "projection_id": (
2333
+ self.prior_projection.projection_id
2334
+ ),
2335
+ "projection_version": (
2336
+ self.prior_projection.projection_version
2337
+ ),
2338
+ "definition_sha256": (
2339
+ self.prior_projection.definition_sha256
2340
+ ),
2341
+ "request_phase": request.phase.value,
2342
+ "prior_means": [
2343
+ {
2344
+ "branch_id": branch_id,
2345
+ "mean_hex": mean.hex(),
2346
+ }
2347
+ for branch_id, mean in sorted(
2348
+ projected_prior_by_branch.items()
2349
+ )
2350
+ ],
2351
+ "applied_before_disagreement_equivalence": True,
2352
+ "candidate_outcomes_observed": False,
2353
+ }
2354
+ ),
2355
+ "disagreement_design": (
2356
+ None
2357
+ if disagreement_design is None
2358
+ else disagreement_design.to_record()
2359
+ ),
2360
+ "all_complete_branches_frozen_before_pilot": True,
2361
+ "outcome_conditioned_regeneration": False,
2362
+ "candidate_outcomes_observed": False,
2363
+ "workload_model_provider_branches": False,
2364
+ }
2365
+ ),
2366
+ )
2367
+
2368
+
2369
+ @dataclass(frozen=True, slots=True)
2370
+ class PortfolioRaceBranchScore:
2371
+ branch_id: str
2372
+ score: float
2373
+ observed_completion_fraction: float
2374
+ lane_posteriors: tuple[tuple[str, float, int], ...]
2375
+
2376
+ def __post_init__(self) -> None:
2377
+ _require_token(self.branch_id, name="branch_id")
2378
+ for value, name in (
2379
+ (self.score, "score"),
2380
+ (
2381
+ self.observed_completion_fraction,
2382
+ "observed_completion_fraction",
2383
+ ),
2384
+ ):
2385
+ if (
2386
+ type(value) is not float
2387
+ or not math.isfinite(value)
2388
+ or not 0.0 <= value <= 1.0
2389
+ ):
2390
+ raise ValueError(f"{name} must be finite and lie in [0, 1]")
2391
+ if (
2392
+ type(self.lane_posteriors) is not tuple
2393
+ or not self.lane_posteriors
2394
+ or self.lane_posteriors
2395
+ != tuple(sorted(self.lane_posteriors))
2396
+ ):
2397
+ raise ValueError("lane_posteriors must be non-empty and canonical")
2398
+ for lane_id, posterior, count in self.lane_posteriors:
2399
+ _require_token(lane_id, name="lane posterior ID")
2400
+ if (
2401
+ type(posterior) is not float
2402
+ or not math.isfinite(posterior)
2403
+ or not 0.0 <= posterior <= 1.0
2404
+ ):
2405
+ raise ValueError("lane posterior must lie in [0, 1]")
2406
+ if type(count) is not int or count < 0:
2407
+ raise ValueError("lane observation count must be non-negative")
2408
+
2409
+ def to_record(self) -> dict[str, object]:
2410
+ self.__post_init__()
2411
+ return {
2412
+ "branch_id": self.branch_id,
2413
+ "score_hex": self.score.hex(),
2414
+ "observed_completion_fraction_hex": (
2415
+ self.observed_completion_fraction.hex()
2416
+ ),
2417
+ "lane_posteriors": [
2418
+ {
2419
+ "lane_id": lane_id,
2420
+ "posterior_mean_hex": posterior.hex(),
2421
+ "observation_count": count,
2422
+ }
2423
+ for lane_id, posterior, count in self.lane_posteriors
2424
+ ],
2425
+ }
2426
+
2427
+
2428
+ @dataclass(frozen=True, slots=True)
2429
+ class PortfolioRaceGateDecision:
2430
+ gate_id: str
2431
+ gate_version: int
2432
+ gate_definition_sha256: str
2433
+ plan_sha256: str
2434
+ pilot_outcome_batch_sha256: str
2435
+ selected_branch_id: str
2436
+ selected_requirement_sha256: str
2437
+ positive_pilot_count: int
2438
+ pilot_count: int
2439
+ branch_scores: tuple[PortfolioRaceBranchScore, ...]
2440
+ evidence: FrozenJsonObject
2441
+ decision_sha256: str = field(init=False)
2442
+
2443
+ def __post_init__(self) -> None:
2444
+ _require_token(self.gate_id, name="gate_id")
2445
+ if type(self.gate_version) is not int or self.gate_version <= 0:
2446
+ raise ValueError("gate_version must be positive")
2447
+ for value, name in (
2448
+ (self.gate_definition_sha256, "gate_definition_sha256"),
2449
+ (self.plan_sha256, "plan_sha256"),
2450
+ (
2451
+ self.pilot_outcome_batch_sha256,
2452
+ "pilot_outcome_batch_sha256",
2453
+ ),
2454
+ (
2455
+ self.selected_requirement_sha256,
2456
+ "selected_requirement_sha256",
2457
+ ),
2458
+ ):
2459
+ require_sha256(value, name)
2460
+ _require_token(self.selected_branch_id, name="selected_branch_id")
2461
+ if (
2462
+ type(self.positive_pilot_count) is not int
2463
+ or type(self.pilot_count) is not int
2464
+ or not 0 <= self.positive_pilot_count <= self.pilot_count
2465
+ ):
2466
+ raise ValueError("pilot counts are inconsistent")
2467
+ if (
2468
+ type(self.branch_scores) is not tuple
2469
+ or not self.branch_scores
2470
+ or tuple(value.branch_id for value in self.branch_scores)
2471
+ != tuple(sorted({value.branch_id for value in self.branch_scores}))
2472
+ ):
2473
+ raise ValueError("branch_scores must be non-empty and canonical")
2474
+ for value in self.branch_scores:
2475
+ if type(value) is not PortfolioRaceBranchScore:
2476
+ raise TypeError("branch_scores must be exact")
2477
+ value.__post_init__()
2478
+ if self.selected_branch_id not in {
2479
+ value.branch_id for value in self.branch_scores
2480
+ }:
2481
+ raise ValueError("selected branch is absent from scores")
2482
+ if (
2483
+ type(self.evidence) is not FrozenJsonObject
2484
+ or freeze_json(self.evidence) is not self.evidence
2485
+ ):
2486
+ raise TypeError("gate evidence must be a frozen object")
2487
+ object.__setattr__(
2488
+ self,
2489
+ "decision_sha256",
2490
+ _hash(_GATE_DECISION_DOMAIN, self._unsigned_record()),
2491
+ )
2492
+
2493
+ def _unsigned_record(self) -> dict[str, object]:
2494
+ return {
2495
+ "schema_version": 1,
2496
+ "gate": {
2497
+ "gate_id": self.gate_id,
2498
+ "gate_version": self.gate_version,
2499
+ "definition_sha256": self.gate_definition_sha256,
2500
+ },
2501
+ "plan_sha256": self.plan_sha256,
2502
+ "pilot_outcome_batch_sha256": (
2503
+ self.pilot_outcome_batch_sha256
2504
+ ),
2505
+ "selected_branch_id": self.selected_branch_id,
2506
+ "selected_requirement_sha256": (
2507
+ self.selected_requirement_sha256
2508
+ ),
2509
+ "positive_pilot_count": self.positive_pilot_count,
2510
+ "pilot_count": self.pilot_count,
2511
+ "branch_scores": [
2512
+ value.to_record() for value in self.branch_scores
2513
+ ],
2514
+ "evidence_sha256": typed_json_sha256(self.evidence),
2515
+ }
2516
+
2517
+ def to_record(self, *, include_evidence: bool = False) -> dict[str, object]:
2518
+ self.__post_init__()
2519
+ record = {
2520
+ **self._unsigned_record(),
2521
+ "decision_sha256": self.decision_sha256,
2522
+ }
2523
+ if include_evidence:
2524
+ record["evidence"] = thaw_json(self.evidence)
2525
+ return record
2526
+
2527
+
2528
+ @dataclass(frozen=True, slots=True)
2529
+ class EvidenceAdaptivePortfolioRaceGate:
2530
+ """Select a frozen slate only when pilot-to-completion transfer is covered.
2531
+
2532
+ A pilot may update only its predeclared projected lane. Adaptive branch
2533
+ choice is additionally disabled unless *every* branch has sufficient
2534
+ completion exposure in observed lanes. The fail-closed decision then uses
2535
+ frozen branch priors, preventing a single non-exchangeable pilot from
2536
+ deciding an entire heterogeneous continuation.
2537
+ """
2538
+
2539
+ prior_strength: float = 1.0
2540
+ minimum_observed_completion_fraction: float = 0.5
2541
+ gate_id: str = EVIDENCE_ADAPTIVE_PORTFOLIO_RACE_GATE_ID
2542
+ gate_version: int = EVIDENCE_ADAPTIVE_PORTFOLIO_RACE_GATE_VERSION
2543
+ definition_sha256: str = field(init=False)
2544
+
2545
+ def __post_init__(self) -> None:
2546
+ if (
2547
+ type(self.prior_strength) is not float
2548
+ or not math.isfinite(self.prior_strength)
2549
+ or self.prior_strength <= 0.0
2550
+ ):
2551
+ raise ValueError("prior_strength must be finite and positive")
2552
+ if (
2553
+ type(self.minimum_observed_completion_fraction) is not float
2554
+ or not math.isfinite(
2555
+ self.minimum_observed_completion_fraction
2556
+ )
2557
+ or not 0.0
2558
+ < self.minimum_observed_completion_fraction
2559
+ <= 1.0
2560
+ ):
2561
+ raise ValueError(
2562
+ "minimum_observed_completion_fraction must lie in (0, 1]"
2563
+ )
2564
+ _require_token(self.gate_id, name="gate_id")
2565
+ if (
2566
+ self.gate_version
2567
+ != EVIDENCE_ADAPTIVE_PORTFOLIO_RACE_GATE_VERSION
2568
+ ):
2569
+ raise ValueError("gate_version is immutable")
2570
+ object.__setattr__(
2571
+ self,
2572
+ "definition_sha256",
2573
+ _hash(
2574
+ _GATE_DEFINITION_DOMAIN,
2575
+ {
2576
+ "schema_version": 1,
2577
+ "gate_id": self.gate_id,
2578
+ "gate_version": self.gate_version,
2579
+ "prior_strength_hex": self.prior_strength.hex(),
2580
+ "minimum_observed_completion_fraction_hex": (
2581
+ self.minimum_observed_completion_fraction.hex()
2582
+ ),
2583
+ "pilot_normalization": "max_positive_marginal_gain",
2584
+ "lane_update": "bounded_consequence_posterior_mean",
2585
+ "branch_score": "completion_exposure_weighted_lane_mean",
2586
+ "adaptive_qualification": (
2587
+ "all_branches_meet_minimum_observed_completion_"
2588
+ "fraction"
2589
+ ),
2590
+ "unqualified_fallback": (
2591
+ "higher_frozen_prior_then_branch_id"
2592
+ ),
2593
+ "tie_break": "higher_score_then_branch_id",
2594
+ "only_pilot_outcomes_observed": True,
2595
+ "workload_model_provider_branches": False,
2596
+ },
2597
+ ),
2598
+ )
2599
+
2600
+ def decide(
2601
+ self,
2602
+ plan: SequentialAllocationPlanPort,
2603
+ outcomes: SequentialPilotOutcomeBatch,
2604
+ ) -> SequentialAllocationGateDecisionPort:
2605
+ self.__post_init__()
2606
+ if type(plan) is not PrecommittedPortfolioRacePlan:
2607
+ raise TypeError("portfolio-race gate requires its exact plan")
2608
+ plan.__post_init__()
2609
+ if type(outcomes) is not SequentialPilotOutcomeBatch:
2610
+ raise TypeError("outcomes must be exact")
2611
+ outcomes.__post_init__()
2612
+ if (
2613
+ outcomes.plan_sha256 != plan.plan_sha256
2614
+ or outcomes.residual_request_sha256
2615
+ != plan.residual_request_sha256
2616
+ or tuple(value.action_sha256 for value in outcomes.outcomes)
2617
+ != plan.pilot_action_sha256s
2618
+ ):
2619
+ raise ValueError("pilot outcomes differ from their race plan")
2620
+ lane_by_action = {
2621
+ value.action_sha256: value.lane_id
2622
+ for value in plan.pilot_lane_bindings
2623
+ }
2624
+ maximum_gain = max(
2625
+ (
2626
+ value.marginal_archive_gain
2627
+ for value in outcomes.outcomes
2628
+ ),
2629
+ default=0.0,
2630
+ )
2631
+ normalized_by_lane: dict[str, list[float]] = {}
2632
+ for outcome in outcomes.outcomes:
2633
+ normalized = (
2634
+ 0.0
2635
+ if maximum_gain == 0.0
2636
+ else outcome.marginal_archive_gain / maximum_gain
2637
+ )
2638
+ normalized_by_lane.setdefault(
2639
+ lane_by_action[outcome.action_sha256],
2640
+ [],
2641
+ ).append(normalized)
2642
+ scores: list[PortfolioRaceBranchScore] = []
2643
+ for branch in plan.branches:
2644
+ weighted = 0.0
2645
+ observed_exposure = 0
2646
+ total_exposure = sum(
2647
+ count for _lane_id, count in branch.completion_lane_exposure
2648
+ )
2649
+ posteriors: list[tuple[str, float, int]] = []
2650
+ for lane_id, exposure in branch.completion_lane_exposure:
2651
+ observations = normalized_by_lane.get(lane_id, [])
2652
+ posterior = (
2653
+ branch.prior_mean * self.prior_strength
2654
+ + math.fsum(observations)
2655
+ ) / (self.prior_strength + len(observations))
2656
+ weighted += exposure * posterior
2657
+ if observations:
2658
+ observed_exposure += exposure
2659
+ posteriors.append((lane_id, posterior, len(observations)))
2660
+ scores.append(
2661
+ PortfolioRaceBranchScore(
2662
+ branch_id=branch.branch_id,
2663
+ score=weighted / total_exposure,
2664
+ observed_completion_fraction=(
2665
+ observed_exposure / total_exposure
2666
+ ),
2667
+ lane_posteriors=tuple(sorted(posteriors)),
2668
+ )
2669
+ )
2670
+ adaptive_selection_qualified = bool(outcomes.outcomes) and all(
2671
+ value.observed_completion_fraction
2672
+ >= self.minimum_observed_completion_fraction
2673
+ for value in scores
2674
+ )
2675
+ if adaptive_selection_qualified:
2676
+ selected_score = min(
2677
+ scores,
2678
+ key=lambda value: (-value.score, value.branch_id),
2679
+ )
2680
+ selected = plan.branch_for(selected_score.branch_id)
2681
+ selection_mode = "coverage_qualified_lane_adaptation"
2682
+ else:
2683
+ selected = min(
2684
+ plan.branches,
2685
+ key=lambda value: (-value.prior_mean, value.branch_id),
2686
+ )
2687
+ selection_mode = "insufficient_coverage_frozen_prior_fallback"
2688
+ positive_count = sum(
2689
+ value.positive_marginal_utility for value in outcomes.outcomes
2690
+ )
2691
+ return PortfolioRaceGateDecision(
2692
+ gate_id=self.gate_id,
2693
+ gate_version=self.gate_version,
2694
+ gate_definition_sha256=self.definition_sha256,
2695
+ plan_sha256=plan.plan_sha256,
2696
+ pilot_outcome_batch_sha256=outcomes.batch_sha256,
2697
+ selected_branch_id=selected.branch_id,
2698
+ selected_requirement_sha256=(
2699
+ selected.requirement.requirement_sha256
2700
+ ),
2701
+ positive_pilot_count=positive_count,
2702
+ pilot_count=len(outcomes.outcomes),
2703
+ branch_scores=tuple(
2704
+ sorted(scores, key=lambda value: value.branch_id)
2705
+ ),
2706
+ evidence=freeze_json(
2707
+ {
2708
+ "maximum_pilot_marginal_gain_hex": maximum_gain.hex(),
2709
+ "minimum_observed_completion_fraction_hex": (
2710
+ self.minimum_observed_completion_fraction.hex()
2711
+ ),
2712
+ "adaptive_selection_qualified": (
2713
+ adaptive_selection_qualified
2714
+ ),
2715
+ "selection_mode": selection_mode,
2716
+ "pilot_to_completion_transfer": (
2717
+ "same_predeclared_projected_lane_only"
2718
+ ),
2719
+ "unobserved_completion_lanes_remain_at_frozen_prior": True,
2720
+ "branch_identity_used_as_exchangeability_lane": False,
2721
+ "only_pilot_outcomes_observed": True,
2722
+ "selected_branch_frozen_before_pilot": True,
2723
+ "all_branch_scores": [
2724
+ value.to_record()
2725
+ for value in sorted(
2726
+ scores,
2727
+ key=lambda item: item.branch_id,
2728
+ )
2729
+ ],
2730
+ "workload_model_provider_branches": False,
2731
+ }
2732
+ ),
2733
+ )
2734
+
2735
+
2736
+ __all__ = [
2737
+ "EVIDENCE_ADAPTIVE_PORTFOLIO_RACE_GATE_ID",
2738
+ "EVIDENCE_ADAPTIVE_PORTFOLIO_RACE_GATE_VERSION",
2739
+ "PHASE_CONDITIONED_PORTFOLIO_RACE_PRIOR_ID",
2740
+ "PHASE_CONDITIONED_PORTFOLIO_RACE_PRIOR_VERSION",
2741
+ "PRECOMMITTED_PORTFOLIO_RACE_PLANNER_ID",
2742
+ "PRECOMMITTED_PORTFOLIO_RACE_PLANNER_VERSION",
2743
+ "SYMMETRIC_DIFFERENCE_PORTFOLIO_RACE_POLICY_ID",
2744
+ "SYMMETRIC_DIFFERENCE_PORTFOLIO_RACE_POLICY_VERSION",
2745
+ "TRACE_FIELD_PORTFOLIO_LANE_PROJECTION_ID",
2746
+ "TRACE_FIELD_PORTFOLIO_LANE_PROJECTION_VERSION",
2747
+ "EvidenceAdaptivePortfolioRaceGate",
2748
+ "FrozenPortfolioRaceBranch",
2749
+ "PhaseConditionedPortfolioRacePrior",
2750
+ "PortfolioLaneProjectionPort",
2751
+ "PortfolioRacePriorProjectionPort",
2752
+ "PortfolioRaceBranchScore",
2753
+ "PortfolioRaceDisagreementDesign",
2754
+ "PortfolioRaceDisagreementPolicyPort",
2755
+ "PortfolioRaceGateDecision",
2756
+ "PortfolioRacePilotLaneBinding",
2757
+ "PortfolioRacePolicyBinding",
2758
+ "PrecommittedPortfolioRacePlan",
2759
+ "PrecommittedPortfolioRacePlanner",
2760
+ "SymmetricDifferencePortfolioRacePolicy",
2761
+ "TraceFieldPortfolioLaneProjection",
2762
+ ]