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,707 @@
1
+ """Precommitted balanced complete-subset blocks for causal memory trials.
2
+
3
+ The planner in this module is deliberately provider- and workload-neutral. It
4
+ binds an immutable :class:`MemoryScoreSnapshot` to an ordered collection of
5
+ stable experimental units *before* any provider request is made. Every full
6
+ block visits each member of the canonical ``C(n, k)`` insight-subset catalog
7
+ exactly once. A trailing partial block samples without replacement from that
8
+ same catalog.
9
+
10
+ Randomness is kept outside the policy. Callers supply exact integer ranks:
11
+
12
+ * one permutation rank in ``[0, C(n,k)!)`` for every complete block;
13
+ * for a remainder of size ``r``, one combination rank in
14
+ ``[0, C(C(n,k),r))`` and one permutation rank in ``[0, r!)``.
15
+
16
+ The planner only maps those ranks to a frozen schedule. Consequently the
17
+ randomization law is exact, easy to replay, and independent of provider
18
+ latency, completion order, evaluator output, or benchmark semantics.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import hashlib
24
+ import itertools
25
+ import json
26
+ import math
27
+ import re
28
+ from dataclasses import dataclass
29
+ from typing import overload
30
+
31
+ from agent_evolve.domain.insight import InsightRef
32
+ from agent_evolve.policies.memory.randomized_subset import (
33
+ InsightSelectionDecision,
34
+ InsightSelectionMode,
35
+ )
36
+ from agent_evolve.policies.memory.staged_causal import (
37
+ DeterministicMemoryControlPolicy,
38
+ MemoryScoreSnapshot,
39
+ insight_selection_decision_sha256,
40
+ )
41
+
42
+
43
+ BALANCED_SUBSET_BLOCK_POLICY_ID = "balanced_complete_k_subset_blocks"
44
+ BALANCED_SUBSET_BLOCK_POLICY_VERSION = 1
45
+ _POLICY_DEFINITION = (
46
+ b"agent-evolve:balanced-complete-k-subset-blocks:v1:"
47
+ b"canonical-lexicographic-catalog;externally-ranked-full-permutations;"
48
+ b"externally-ranked-no-replacement-remainder;immutable-snapshot-and-units;"
49
+ b"uniform-control-materialization;treated-and-control-support-preflight"
50
+ )
51
+ BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256 = hashlib.sha256(
52
+ _POLICY_DEFINITION
53
+ ).hexdigest()
54
+ _PLAN_RECEIPT_DOMAIN = b"agent-evolve:balanced-subset-block-plan:v1\x00"
55
+ _SAFE_UNIT_TOKEN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$")
56
+
57
+
58
+ def _canonical_json(value: object) -> bytes:
59
+ return json.dumps(
60
+ value,
61
+ allow_nan=False,
62
+ ensure_ascii=True,
63
+ separators=(",", ":"),
64
+ sort_keys=True,
65
+ ).encode("ascii", errors="strict")
66
+
67
+
68
+ def _hash_record(domain: bytes, value: object) -> str:
69
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
70
+
71
+
72
+ def _reference_record(reference: InsightRef) -> dict[str, object]:
73
+ return {
74
+ "insight_id": reference.insight_id.value,
75
+ "version": reference.version,
76
+ }
77
+
78
+
79
+ def _require_unit_token(value: str, name: str) -> None:
80
+ if type(value) is not str or _SAFE_UNIT_TOKEN.fullmatch(value) is None:
81
+ raise ValueError(f"{name} must be a bounded durable identifier")
82
+
83
+
84
+ def _require_rank(rank: int, *, stop: int, name: str) -> None:
85
+ if type(rank) is not int or rank < 0 or rank >= stop:
86
+ raise ValueError(f"{name} must lie in [0, {stop})")
87
+
88
+
89
+ def _unrank_combination(
90
+ values: tuple[int, ...],
91
+ subset_size: int,
92
+ rank: int,
93
+ *,
94
+ name: str,
95
+ ) -> tuple[int, ...]:
96
+ """Map one exact lexicographic combination rank to its members."""
97
+
98
+ combination_count = math.comb(len(values), subset_size)
99
+ _require_rank(rank, stop=combination_count, name=name)
100
+ selected_indices: list[int] = []
101
+ remaining_rank = rank
102
+ start = 0
103
+ for position in range(subset_size):
104
+ remaining_positions = subset_size - position - 1
105
+ for index in range(start, len(values)):
106
+ suffix_count = math.comb(
107
+ len(values) - index - 1,
108
+ remaining_positions,
109
+ )
110
+ if remaining_rank < suffix_count:
111
+ selected_indices.append(index)
112
+ start = index + 1
113
+ break
114
+ remaining_rank -= suffix_count
115
+ return tuple(values[index] for index in selected_indices)
116
+
117
+
118
+ def _unrank_permutation(
119
+ values: tuple[int, ...],
120
+ rank: int,
121
+ *,
122
+ name: str,
123
+ ) -> tuple[int, ...]:
124
+ """Map one exact Lehmer rank to a permutation of ``values``."""
125
+
126
+ permutation_count = math.factorial(len(values))
127
+ _require_rank(rank, stop=permutation_count, name=name)
128
+ remaining = list(values)
129
+ result: list[int] = []
130
+ remaining_rank = rank
131
+ for slots in range(len(values), 0, -1):
132
+ block_size = math.factorial(slots - 1)
133
+ index, remaining_rank = divmod(remaining_rank, block_size)
134
+ result.append(remaining.pop(index))
135
+ return tuple(result)
136
+
137
+
138
+ def canonical_memory_subset_catalog(
139
+ snapshot: MemoryScoreSnapshot,
140
+ subset_size: int,
141
+ ) -> tuple[tuple[InsightRef, ...], ...]:
142
+ """Return the canonical lexicographic ``C(n,k)`` catalog for a snapshot."""
143
+
144
+ if not isinstance(snapshot, MemoryScoreSnapshot):
145
+ raise TypeError("snapshot must be a MemoryScoreSnapshot")
146
+ references = tuple(entry.reference for entry in snapshot.entries)
147
+ if type(subset_size) is not int:
148
+ raise TypeError("subset_size must be an exact integer")
149
+ if subset_size <= 0 or subset_size >= len(references):
150
+ raise ValueError(
151
+ "subset_size must lie strictly between zero and the snapshot size "
152
+ "so treated and control support are possible"
153
+ )
154
+ return tuple(itertools.combinations(references, subset_size))
155
+
156
+
157
+ @dataclass(frozen=True, slots=True)
158
+ class StableMemoryAssignmentUnit:
159
+ """One prospectively named provider-call slot in the frozen schedule."""
160
+
161
+ unit_key: str
162
+ generation: int
163
+ lane_id: str
164
+
165
+ def __post_init__(self) -> None:
166
+ _require_unit_token(self.unit_key, "unit_key")
167
+ if type(self.generation) is not int or self.generation < 0:
168
+ raise ValueError("generation must be a non-negative exact integer")
169
+ _require_unit_token(self.lane_id, "lane_id")
170
+
171
+ def to_record(self) -> dict[str, object]:
172
+ self.__post_init__()
173
+ return {
174
+ "unit_key": self.unit_key,
175
+ "generation": self.generation,
176
+ "lane_id": self.lane_id,
177
+ }
178
+
179
+
180
+ @dataclass(frozen=True, slots=True)
181
+ class MemorySubsetSupport:
182
+ """Realized selected/unselected support for one exact insight version."""
183
+
184
+ reference: InsightRef
185
+ treated_count: int
186
+ control_count: int
187
+
188
+ def __post_init__(self) -> None:
189
+ if not isinstance(self.reference, InsightRef):
190
+ raise TypeError("reference must be an InsightRef")
191
+ for name in ("treated_count", "control_count"):
192
+ value = getattr(self, name)
193
+ if type(value) is not int or value < 0:
194
+ raise ValueError(f"{name} must be a non-negative exact integer")
195
+
196
+ @property
197
+ def has_overlap(self) -> bool:
198
+ """Whether both treated and control observations are scheduled."""
199
+
200
+ return self.treated_count > 0 and self.control_count > 0
201
+
202
+ def to_record(self) -> dict[str, object]:
203
+ self.__post_init__()
204
+ return {
205
+ "reference": _reference_record(self.reference),
206
+ "treated_count": self.treated_count,
207
+ "control_count": self.control_count,
208
+ "has_overlap": self.has_overlap,
209
+ }
210
+
211
+
212
+ @dataclass(frozen=True, slots=True)
213
+ class BalancedSubsetBlockAssignment:
214
+ """One stable unit's exact catalog assignment and uniform decision."""
215
+
216
+ schedule_position: int
217
+ unit: StableMemoryAssignmentUnit
218
+ block_index: int
219
+ block_position: int
220
+ is_remainder: bool
221
+ subset_rank: int
222
+ decision: InsightSelectionDecision
223
+
224
+ def __post_init__(self) -> None:
225
+ for name in (
226
+ "schedule_position",
227
+ "block_index",
228
+ "block_position",
229
+ "subset_rank",
230
+ ):
231
+ value = getattr(self, name)
232
+ if type(value) is not int or value < 0:
233
+ raise ValueError(f"{name} must be a non-negative exact integer")
234
+ if type(self.unit) is not StableMemoryAssignmentUnit:
235
+ raise TypeError("unit must be an exact StableMemoryAssignmentUnit")
236
+ self.unit.__post_init__()
237
+ if type(self.is_remainder) is not bool:
238
+ raise TypeError("is_remainder must be a bool")
239
+ if not isinstance(self.decision, InsightSelectionDecision):
240
+ raise TypeError("decision must be an InsightSelectionDecision")
241
+ if self.decision.mode is not InsightSelectionMode.EXPLORE_UNIFORM:
242
+ raise ValueError("balanced block assignments must use uniform controls")
243
+
244
+ @property
245
+ def selection_decision_sha256(self) -> str:
246
+ return insight_selection_decision_sha256(self.decision)
247
+
248
+ def to_record(self) -> dict[str, object]:
249
+ self.__post_init__()
250
+ probability = self.decision.selected_subset_probability
251
+ return {
252
+ "schedule_position": self.schedule_position,
253
+ "unit": self.unit.to_record(),
254
+ "block_index": self.block_index,
255
+ "block_position": self.block_position,
256
+ "is_remainder": self.is_remainder,
257
+ "subset_rank": self.subset_rank,
258
+ "selected": [
259
+ _reference_record(reference) for reference in self.decision.selected
260
+ ],
261
+ "selected_subset_probability": [
262
+ probability.numerator,
263
+ probability.denominator,
264
+ ],
265
+ "selection_decision_sha256": self.selection_decision_sha256,
266
+ }
267
+
268
+
269
+ def _validate_ordered_units(
270
+ ordered_units: tuple[StableMemoryAssignmentUnit, ...],
271
+ ) -> None:
272
+ if type(ordered_units) is not tuple or not ordered_units:
273
+ raise ValueError("ordered_units must be a non-empty exact tuple")
274
+ if any(type(unit) is not StableMemoryAssignmentUnit for unit in ordered_units):
275
+ raise TypeError(
276
+ "ordered_units must contain exact StableMemoryAssignmentUnit values"
277
+ )
278
+ for unit in ordered_units:
279
+ unit.__post_init__()
280
+ unit_keys = tuple(unit.unit_key for unit in ordered_units)
281
+ if len(set(unit_keys)) != len(unit_keys):
282
+ raise ValueError("ordered_units cannot repeat a stable unit_key")
283
+ generation_lanes = tuple((unit.generation, unit.lane_id) for unit in ordered_units)
284
+ if len(set(generation_lanes)) != len(generation_lanes):
285
+ raise ValueError("ordered_units cannot repeat a generation/lane pair")
286
+
287
+
288
+ def _scheduled_subset_ranks(
289
+ *,
290
+ unit_count: int,
291
+ catalog_size: int,
292
+ full_block_permutation_ranks: tuple[int, ...],
293
+ remainder_selection_rank: int | None,
294
+ remainder_permutation_rank: int | None,
295
+ ) -> tuple[int, ...]:
296
+ full_block_count, remainder_size = divmod(unit_count, catalog_size)
297
+ if type(full_block_permutation_ranks) is not tuple:
298
+ raise TypeError("full_block_permutation_ranks must be an exact tuple")
299
+ if len(full_block_permutation_ranks) != full_block_count:
300
+ raise ValueError(
301
+ "full_block_permutation_ranks must provide exactly one rank per "
302
+ "complete catalog block"
303
+ )
304
+
305
+ catalog_ranks = tuple(range(catalog_size))
306
+ schedule: list[int] = []
307
+ for block_index, permutation_rank in enumerate(full_block_permutation_ranks):
308
+ schedule.extend(
309
+ _unrank_permutation(
310
+ catalog_ranks,
311
+ permutation_rank,
312
+ name=f"full_block_permutation_ranks[{block_index}]",
313
+ )
314
+ )
315
+
316
+ if remainder_size == 0:
317
+ if remainder_selection_rank is not None:
318
+ raise ValueError(
319
+ "remainder_selection_rank must be None when there is no remainder"
320
+ )
321
+ if remainder_permutation_rank is not None:
322
+ raise ValueError(
323
+ "remainder_permutation_rank must be None when there is no remainder"
324
+ )
325
+ return tuple(schedule)
326
+
327
+ if remainder_selection_rank is None or remainder_permutation_rank is None:
328
+ raise ValueError(
329
+ "a partial block requires exact remainder selection and permutation ranks"
330
+ )
331
+ selected = _unrank_combination(
332
+ catalog_ranks,
333
+ remainder_size,
334
+ remainder_selection_rank,
335
+ name="remainder_selection_rank",
336
+ )
337
+ schedule.extend(
338
+ _unrank_permutation(
339
+ selected,
340
+ remainder_permutation_rank,
341
+ name="remainder_permutation_rank",
342
+ )
343
+ )
344
+ return tuple(schedule)
345
+
346
+
347
+ def _support_for(
348
+ references: tuple[InsightRef, ...],
349
+ assignments: tuple[BalancedSubsetBlockAssignment, ...],
350
+ ) -> tuple[MemorySubsetSupport, ...]:
351
+ return tuple(
352
+ MemorySubsetSupport(
353
+ reference=reference,
354
+ treated_count=sum(
355
+ reference in assignment.decision.selected for assignment in assignments
356
+ ),
357
+ control_count=sum(
358
+ reference not in assignment.decision.selected
359
+ for assignment in assignments
360
+ ),
361
+ )
362
+ for reference in references
363
+ )
364
+
365
+
366
+ @dataclass(frozen=True, slots=True, eq=False)
367
+ class BalancedSubsetBlockPlan:
368
+ """Authenticated pre-provider schedule and its replay receipt."""
369
+
370
+ snapshot: MemoryScoreSnapshot
371
+ ordered_units: tuple[StableMemoryAssignmentUnit, ...]
372
+ subset_size: int
373
+ catalog: tuple[tuple[InsightRef, ...], ...]
374
+ full_block_permutation_ranks: tuple[int, ...]
375
+ remainder_selection_rank: int | None
376
+ remainder_permutation_rank: int | None
377
+ assignments: tuple[BalancedSubsetBlockAssignment, ...]
378
+ support: tuple[MemorySubsetSupport, ...]
379
+ policy_id: str = BALANCED_SUBSET_BLOCK_POLICY_ID
380
+ policy_version: int = BALANCED_SUBSET_BLOCK_POLICY_VERSION
381
+ policy_definition_sha256: str = BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256
382
+
383
+ def __post_init__(self) -> None:
384
+ if not isinstance(self.snapshot, MemoryScoreSnapshot):
385
+ raise TypeError("snapshot must be a MemoryScoreSnapshot")
386
+ _validate_ordered_units(self.ordered_units)
387
+ expected_catalog = canonical_memory_subset_catalog(
388
+ self.snapshot,
389
+ self.subset_size,
390
+ )
391
+ if self.catalog != expected_catalog:
392
+ raise ValueError("catalog is not the canonical C(n,k) subset catalog")
393
+ if self.policy_id != BALANCED_SUBSET_BLOCK_POLICY_ID:
394
+ raise ValueError("plan names an unsupported policy_id")
395
+ if self.policy_version != BALANCED_SUBSET_BLOCK_POLICY_VERSION:
396
+ raise ValueError("plan names an unsupported policy_version")
397
+ if (
398
+ self.policy_definition_sha256
399
+ != BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256
400
+ ):
401
+ raise ValueError("balanced subset-block policy definition changed")
402
+
403
+ scheduled_ranks = _scheduled_subset_ranks(
404
+ unit_count=len(self.ordered_units),
405
+ catalog_size=len(self.catalog),
406
+ full_block_permutation_ranks=self.full_block_permutation_ranks,
407
+ remainder_selection_rank=self.remainder_selection_rank,
408
+ remainder_permutation_rank=self.remainder_permutation_rank,
409
+ )
410
+ if type(self.assignments) is not tuple or len(self.assignments) != len(
411
+ self.ordered_units
412
+ ):
413
+ raise ValueError("assignments must cover every ordered unit exactly once")
414
+ if any(
415
+ type(value) is not BalancedSubsetBlockAssignment
416
+ for value in self.assignments
417
+ ):
418
+ raise TypeError(
419
+ "assignments must contain exact BalancedSubsetBlockAssignment values"
420
+ )
421
+
422
+ controls = DeterministicMemoryControlPolicy()
423
+ catalog_size = len(self.catalog)
424
+ full_block_count = len(self.ordered_units) // catalog_size
425
+ for position, (unit, subset_rank, assignment) in enumerate(
426
+ zip(
427
+ self.ordered_units,
428
+ scheduled_ranks,
429
+ self.assignments,
430
+ strict=True,
431
+ )
432
+ ):
433
+ assignment.__post_init__()
434
+ expected_decision = controls.uniform(
435
+ snapshot=self.snapshot,
436
+ subset_size=self.subset_size,
437
+ subset_rank=subset_rank,
438
+ )
439
+ expected_block_index, expected_block_position = divmod(
440
+ position,
441
+ catalog_size,
442
+ )
443
+ expected_remainder = expected_block_index >= full_block_count
444
+ if (
445
+ assignment.schedule_position != position
446
+ or assignment.unit != unit
447
+ or assignment.block_index != expected_block_index
448
+ or assignment.block_position != expected_block_position
449
+ or assignment.is_remainder is not expected_remainder
450
+ or assignment.subset_rank != subset_rank
451
+ or assignment.decision != expected_decision
452
+ ):
453
+ raise ValueError(
454
+ "assignment does not replay from the frozen snapshot, units, "
455
+ "and exact ranks"
456
+ )
457
+ if assignment.decision.selected != self.catalog[subset_rank]:
458
+ raise ValueError("assignment decision differs from its catalog subset")
459
+
460
+ references = tuple(entry.reference for entry in self.snapshot.entries)
461
+ expected_support = _support_for(references, self.assignments)
462
+ if self.support != expected_support:
463
+ raise ValueError("support report does not match the frozen assignments")
464
+ unsupported = tuple(value for value in self.support if not value.has_overlap)
465
+ if unsupported:
466
+ details = ", ".join(
467
+ f"{value.reference.insight_id.value}@{value.reference.version}"
468
+ f"(treated={value.treated_count},control={value.control_count})"
469
+ for value in unsupported
470
+ )
471
+ raise ValueError(
472
+ "balanced subset-block preflight requires treated and control "
473
+ f"support for every insight; missing overlap: {details}"
474
+ )
475
+
476
+ @property
477
+ def full_block_count(self) -> int:
478
+ return len(self.ordered_units) // len(self.catalog)
479
+
480
+ @property
481
+ def remainder_size(self) -> int:
482
+ return len(self.ordered_units) % len(self.catalog)
483
+
484
+ @overload
485
+ def assignment_for(
486
+ self,
487
+ generation: int,
488
+ lane_id: str,
489
+ *,
490
+ unit_key: None = None,
491
+ ) -> BalancedSubsetBlockAssignment: ...
492
+
493
+ @overload
494
+ def assignment_for(
495
+ self,
496
+ generation: str,
497
+ lane_id: None = None,
498
+ *,
499
+ unit_key: None = None,
500
+ ) -> BalancedSubsetBlockAssignment: ...
501
+
502
+ @overload
503
+ def assignment_for(
504
+ self,
505
+ generation: None = None,
506
+ lane_id: None = None,
507
+ *,
508
+ unit_key: str,
509
+ ) -> BalancedSubsetBlockAssignment: ...
510
+
511
+ def assignment_for(
512
+ self,
513
+ generation: int | str | None = None,
514
+ lane_id: str | None = None,
515
+ *,
516
+ unit_key: str | None = None,
517
+ ) -> BalancedSubsetBlockAssignment:
518
+ """Resolve by ``(generation, lane_id)`` or by one stable unit key."""
519
+
520
+ self.__post_init__()
521
+ if unit_key is not None:
522
+ if generation is not None or lane_id is not None:
523
+ raise ValueError(
524
+ "unit_key cannot be combined with generation or lane_id"
525
+ )
526
+ _require_unit_token(unit_key, "unit_key")
527
+ matches = tuple(
528
+ value for value in self.assignments if value.unit.unit_key == unit_key
529
+ )
530
+ elif type(generation) is str and lane_id is None:
531
+ _require_unit_token(generation, "unit_key")
532
+ matches = tuple(
533
+ value for value in self.assignments if value.unit.unit_key == generation
534
+ )
535
+ else:
536
+ if type(generation) is not int or generation < 0:
537
+ raise ValueError(
538
+ "generation must be a non-negative exact integer when "
539
+ "resolving by lane"
540
+ )
541
+ if lane_id is None:
542
+ raise ValueError("lane_id is required when resolving by generation")
543
+ _require_unit_token(lane_id, "lane_id")
544
+ matches = tuple(
545
+ value
546
+ for value in self.assignments
547
+ if value.unit.generation == generation and value.unit.lane_id == lane_id
548
+ )
549
+ if not matches:
550
+ raise KeyError("no balanced memory assignment matches the stable unit")
551
+ if len(matches) != 1: # pragma: no cover - plan validation forbids this.
552
+ raise RuntimeError("stable memory assignment identity is ambiguous")
553
+ return matches[0]
554
+
555
+ def _unsigned_record(self) -> dict[str, object]:
556
+ return {
557
+ "schema_version": 1,
558
+ "policy": {
559
+ "policy_id": self.policy_id,
560
+ "policy_version": self.policy_version,
561
+ "policy_definition_sha256": self.policy_definition_sha256,
562
+ },
563
+ "score_snapshot_sha256": self.snapshot.snapshot_sha256,
564
+ "subset_size": self.subset_size,
565
+ "catalog": [
566
+ [_reference_record(reference) for reference in subset]
567
+ for subset in self.catalog
568
+ ],
569
+ "ordered_units": [unit.to_record() for unit in self.ordered_units],
570
+ "exact_ranks": {
571
+ "full_block_permutation_ranks": list(self.full_block_permutation_ranks),
572
+ "remainder_selection_rank": self.remainder_selection_rank,
573
+ "remainder_permutation_rank": self.remainder_permutation_rank,
574
+ },
575
+ "full_block_count": self.full_block_count,
576
+ "remainder_size": self.remainder_size,
577
+ "assignments": [value.to_record() for value in self.assignments],
578
+ "support": [value.to_record() for value in self.support],
579
+ }
580
+
581
+ @property
582
+ def receipt_sha256(self) -> str:
583
+ """Digest authenticating the complete prospective plan and receipt."""
584
+
585
+ self.__post_init__()
586
+ return _hash_record(_PLAN_RECEIPT_DOMAIN, self._unsigned_record())
587
+
588
+ @property
589
+ def plan_sha256(self) -> str:
590
+ """Alias emphasizing that the receipt authenticates the whole plan."""
591
+
592
+ return self.receipt_sha256
593
+
594
+ def to_record(self) -> dict[str, object]:
595
+ self.__post_init__()
596
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
597
+
598
+ def __eq__(self, other: object) -> bool:
599
+ return (
600
+ type(other) is BalancedSubsetBlockPlan
601
+ and self.receipt_sha256 == other.receipt_sha256
602
+ )
603
+
604
+ __hash__ = None
605
+
606
+
607
+ @dataclass(frozen=True, slots=True)
608
+ class BalancedSubsetBlockPlanner:
609
+ """Map externally generated exact ranks to a frozen balanced schedule."""
610
+
611
+ def plan(
612
+ self,
613
+ *,
614
+ snapshot: MemoryScoreSnapshot,
615
+ ordered_units: tuple[StableMemoryAssignmentUnit, ...],
616
+ subset_size: int,
617
+ full_block_permutation_ranks: tuple[int, ...],
618
+ remainder_selection_rank: int | None = None,
619
+ remainder_permutation_rank: int | None = None,
620
+ ) -> BalancedSubsetBlockPlan:
621
+ """Preflight, materialize, and authenticate one prospective schedule.
622
+
623
+ Complete blocks are contiguous slices of ``ordered_units``. A caller
624
+ that wants one complete block per stable lane should therefore supply
625
+ lane-major units; no lane semantics are baked into this policy.
626
+ """
627
+
628
+ catalog = canonical_memory_subset_catalog(snapshot, subset_size)
629
+ _validate_ordered_units(ordered_units)
630
+ scheduled_ranks = _scheduled_subset_ranks(
631
+ unit_count=len(ordered_units),
632
+ catalog_size=len(catalog),
633
+ full_block_permutation_ranks=full_block_permutation_ranks,
634
+ remainder_selection_rank=remainder_selection_rank,
635
+ remainder_permutation_rank=remainder_permutation_rank,
636
+ )
637
+ controls = DeterministicMemoryControlPolicy()
638
+ full_block_count = len(ordered_units) // len(catalog)
639
+ assignments = tuple(
640
+ BalancedSubsetBlockAssignment(
641
+ schedule_position=position,
642
+ unit=unit,
643
+ block_index=position // len(catalog),
644
+ block_position=position % len(catalog),
645
+ is_remainder=(position // len(catalog)) >= full_block_count,
646
+ subset_rank=subset_rank,
647
+ decision=controls.uniform(
648
+ snapshot=snapshot,
649
+ subset_size=subset_size,
650
+ subset_rank=subset_rank,
651
+ ),
652
+ )
653
+ for position, (unit, subset_rank) in enumerate(
654
+ zip(ordered_units, scheduled_ranks, strict=True)
655
+ )
656
+ )
657
+ references = tuple(entry.reference for entry in snapshot.entries)
658
+ return BalancedSubsetBlockPlan(
659
+ snapshot=snapshot,
660
+ ordered_units=ordered_units,
661
+ subset_size=subset_size,
662
+ catalog=catalog,
663
+ full_block_permutation_ranks=full_block_permutation_ranks,
664
+ remainder_selection_rank=remainder_selection_rank,
665
+ remainder_permutation_rank=remainder_permutation_rank,
666
+ assignments=assignments,
667
+ support=_support_for(references, assignments),
668
+ )
669
+
670
+ def replay(
671
+ self,
672
+ receipt: BalancedSubsetBlockPlan,
673
+ *,
674
+ snapshot: MemoryScoreSnapshot,
675
+ ordered_units: tuple[StableMemoryAssignmentUnit, ...],
676
+ ) -> BalancedSubsetBlockPlan:
677
+ """Rebuild a receipt from its ranks and reject any basis mismatch."""
678
+
679
+ if type(receipt) is not BalancedSubsetBlockPlan:
680
+ raise TypeError("receipt must be an exact BalancedSubsetBlockPlan")
681
+ receipt.__post_init__()
682
+ replayed = self.plan(
683
+ snapshot=snapshot,
684
+ ordered_units=ordered_units,
685
+ subset_size=receipt.subset_size,
686
+ full_block_permutation_ranks=(receipt.full_block_permutation_ranks),
687
+ remainder_selection_rank=receipt.remainder_selection_rank,
688
+ remainder_permutation_rank=receipt.remainder_permutation_rank,
689
+ )
690
+ if replayed.receipt_sha256 != receipt.receipt_sha256:
691
+ raise ValueError(
692
+ "replayed balanced subset-block plan does not match the receipt"
693
+ )
694
+ return replayed
695
+
696
+
697
+ __all__ = [
698
+ "BALANCED_SUBSET_BLOCK_POLICY_DEFINITION_SHA256",
699
+ "BALANCED_SUBSET_BLOCK_POLICY_ID",
700
+ "BALANCED_SUBSET_BLOCK_POLICY_VERSION",
701
+ "BalancedSubsetBlockAssignment",
702
+ "BalancedSubsetBlockPlan",
703
+ "BalancedSubsetBlockPlanner",
704
+ "MemorySubsetSupport",
705
+ "StableMemoryAssignmentUnit",
706
+ "canonical_memory_subset_catalog",
707
+ ]