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,1812 @@
1
+ """Closed-loop lifecycle for reflected campaign insights.
2
+
3
+ This module joins three evidence channels without conflating them:
4
+
5
+ * a reflection creates an unverified card that remains quarantined;
6
+ * a campaign admission barrier may expose that exact version only to a
7
+ randomized diagnostic assignment;
8
+ * a later generation barrier supplies causal search-utility evidence, while a
9
+ separate global audit supplies semantic support or counterexamples.
10
+
11
+ Normal retrieval becomes possible only when both semantic support and minimum
12
+ causal usefulness are present. All decisions in one generation are prepared
13
+ before the memory bank publishes a canonical lifecycle batch.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import hashlib
19
+ import json
20
+ import math
21
+ from dataclasses import dataclass, field
22
+ from enum import Enum
23
+ from fractions import Fraction
24
+
25
+ from agent_evolve.application.insight_memory import (
26
+ InsightLifecycleChangeRequest,
27
+ InsightLifecycleState,
28
+ InsightMemoryBank,
29
+ InsightMemoryEntry,
30
+ InsightOrigin,
31
+ QuarantineTestAdmissionReceipt,
32
+ )
33
+ from agent_evolve.application.portfolio_evolution import (
34
+ PortfolioMemoryCreditBatchReceipt,
35
+ )
36
+ from agent_evolve.domain.insight import InsightRef
37
+ from agent_evolve.domain.patch import require_sha256
38
+ from agent_evolve.policies.memory.global_falsification import (
39
+ GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256,
40
+ GLOBAL_FALSIFICATION_POLICY_ID,
41
+ GLOBAL_FALSIFICATION_POLICY_VERSION,
42
+ EvidenceDisposition,
43
+ GlobalHypothesisAuditReceipt,
44
+ GlobalHypothesisAuditRequest,
45
+ GlobalHypothesisVerdict,
46
+ HypothesisAuditScope,
47
+ HypothesisClaimStrength,
48
+ HypothesisMetricPrediction,
49
+ TypedEvidencePredicate,
50
+ TypedInterventionSignature,
51
+ )
52
+ from agent_evolve.policies.memory.randomized_subset import (
53
+ InsightTrial,
54
+ MarginalEffectEstimate,
55
+ estimate_marginal_effect,
56
+ )
57
+ from agent_evolve.policies.memory.staged_causal import (
58
+ insight_selection_decision_sha256,
59
+ )
60
+ from agent_evolve.ports.agentic_generator import (
61
+ MetricEffectDirection,
62
+ ReflectionInsightKind,
63
+ )
64
+
65
+
66
+ _REGISTRATION_DOMAIN = b"agent-evolve:campaign-insight-registration:v2\x00"
67
+ _ADMISSION_DOMAIN = b"agent-evolve:campaign-diagnostic-admission:v1\x00"
68
+ _CAUSAL_DOMAIN = b"agent-evolve:campaign-causal-usefulness:v2\x00"
69
+ _PROMOTION_EVIDENCE_DOMAIN = (
70
+ b"agent-evolve:campaign-randomized-promotion-evidence:v1\x00"
71
+ )
72
+ _DECISION_DOMAIN = b"agent-evolve:campaign-insight-lifecycle-decision:v1\x00"
73
+ _BARRIER_DOMAIN = b"agent-evolve:campaign-learning-barrier:v1\x00"
74
+ _POLICY_DOMAIN = b"agent-evolve:campaign-insight-promotion-policy:v2\x00"
75
+ _SEMANTIC_AUDIT_PLAN_DOMAIN = b"agent-evolve:campaign-semantic-audit-plan:v1\x00"
76
+ _PREPARED_BARRIER_DOMAIN = b"agent-evolve:campaign-prepared-learning-barrier:v1\x00"
77
+
78
+
79
+ def _exact_one_sided_sign_p_value(*, successes: int, blocks: int) -> Fraction:
80
+ """Exact upper binomial tail under a fair-sign null.
81
+
82
+ A block whose effect is equal to the practical margin is conservatively a
83
+ failure. The returned rational is retained in receipts so promotion does
84
+ not depend on platform-specific special functions or rounded p-values.
85
+ """
86
+
87
+ if type(successes) is not int or type(blocks) is not int:
88
+ raise TypeError("sign-test counts must be exact integers")
89
+ if successes < 0 or blocks < 0 or successes > blocks:
90
+ raise ValueError("sign-test counts are invalid")
91
+ if blocks == 0:
92
+ return Fraction(1, 1)
93
+ return Fraction(
94
+ sum(math.comb(blocks, value) for value in range(successes, blocks + 1)),
95
+ 2**blocks,
96
+ )
97
+
98
+
99
+ def _canonical_json(value: object) -> bytes:
100
+ return json.dumps(
101
+ value,
102
+ allow_nan=False,
103
+ ensure_ascii=True,
104
+ separators=(",", ":"),
105
+ sort_keys=True,
106
+ ).encode("ascii", errors="strict")
107
+
108
+
109
+ def _hash(domain: bytes, value: object) -> str:
110
+ return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
111
+
112
+
113
+ def _reference_record(reference: InsightRef) -> dict[str, object]:
114
+ return {
115
+ "insight_id": reference.insight_id.value,
116
+ "version": reference.version,
117
+ }
118
+
119
+
120
+ def _trial_record(trial: InsightTrial) -> dict[str, object]:
121
+ InsightTrial.__post_init__(trial)
122
+ return {
123
+ "credit_unit_id": trial.credit_unit_id.value,
124
+ "candidate_ids": [value.value for value in trial.candidate_ids],
125
+ "reward_definition_sha256": trial.reward_definition_hash,
126
+ "selection_decision_sha256": insight_selection_decision_sha256(trial.decision),
127
+ "reward_hex": trial.reward.hex(),
128
+ "treatment_binding_sha256": trial.treatment_binding_sha256,
129
+ }
130
+
131
+
132
+ @dataclass(frozen=True, slots=True)
133
+ class CampaignSemanticAuditPlan:
134
+ """Pre-outcome identity for the exact semantic claim that may be audited.
135
+
136
+ Registry identity and the inclusive audit cutoff are intentionally absent:
137
+ those values exist only once the later evidence universe is sealed. Every
138
+ semantic degree of freedom, including the origin cutoff, is fixed before a
139
+ quarantined card may enter diagnostic testing.
140
+
141
+ The plan reuses the global falsification layer's typed predicate,
142
+ intervention, prediction, strength, and scope constructs. It deliberately
143
+ does not reuse the parent-bound executable-treatment compilation receipt:
144
+ that receipt answers whether one current finite palette can execute a card,
145
+ whereas this plan identifies the claim audited across a sealed registry.
146
+ """
147
+
148
+ reference: InsightRef
149
+ draft_content_sha256: str
150
+ draft_hypothesis_sha256: str
151
+ trigger: TypedEvidencePredicate
152
+ intervention: TypedInterventionSignature
153
+ predictions: tuple[HypothesisMetricPrediction, ...]
154
+ claim_strength: HypothesisClaimStrength
155
+ scope: HypothesisAuditScope
156
+ matcher_definition_sha256: str
157
+ origin_cutoff_event_index: int
158
+ minimum_support_clusters: int
159
+ minimum_support_instances: int
160
+ audit_policy_definition_sha256: str = GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256
161
+ plan_sha256: str = field(init=False)
162
+
163
+ def __post_init__(self) -> None:
164
+ if type(self.reference) is not InsightRef:
165
+ raise TypeError("reference must be an exact InsightRef")
166
+ InsightRef.__post_init__(self.reference)
167
+ require_sha256(self.draft_content_sha256, "draft_content_sha256")
168
+ require_sha256(self.draft_hypothesis_sha256, "draft_hypothesis_sha256")
169
+ if type(self.trigger) is not TypedEvidencePredicate:
170
+ raise TypeError("trigger must be an exact TypedEvidencePredicate")
171
+ TypedEvidencePredicate.__post_init__(self.trigger)
172
+ if type(self.intervention) is not TypedInterventionSignature:
173
+ raise TypeError("intervention must be an exact TypedInterventionSignature")
174
+ TypedInterventionSignature.__post_init__(self.intervention)
175
+ if (
176
+ type(self.predictions) is not tuple
177
+ or not self.predictions
178
+ or any(
179
+ type(value) is not HypothesisMetricPrediction
180
+ for value in self.predictions
181
+ )
182
+ ):
183
+ raise ValueError("predictions must contain exact typed predictions")
184
+ for prediction in self.predictions:
185
+ HypothesisMetricPrediction.__post_init__(prediction)
186
+ if type(self.claim_strength) is not HypothesisClaimStrength:
187
+ raise TypeError("claim_strength must be exact")
188
+ HypothesisClaimStrength.__post_init__(self.claim_strength)
189
+ if type(self.scope) is not HypothesisAuditScope:
190
+ raise TypeError("scope must be exact")
191
+ HypothesisAuditScope.__post_init__(self.scope)
192
+ require_sha256(self.matcher_definition_sha256, "matcher_definition_sha256")
193
+ if (
194
+ type(self.origin_cutoff_event_index) is not int
195
+ or self.origin_cutoff_event_index < 0
196
+ ):
197
+ raise ValueError("origin_cutoff_event_index must be non-negative")
198
+ for name in ("minimum_support_clusters", "minimum_support_instances"):
199
+ value = getattr(self, name)
200
+ if type(value) is not int or value <= 0:
201
+ raise ValueError(f"{name} must be a positive exact integer")
202
+ if (
203
+ self.audit_policy_definition_sha256
204
+ != GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256
205
+ ):
206
+ raise ValueError("unsupported global falsification policy definition")
207
+ object.__setattr__(
208
+ self,
209
+ "plan_sha256",
210
+ _hash(_SEMANTIC_AUDIT_PLAN_DOMAIN, self._unsigned_record()),
211
+ )
212
+
213
+ @classmethod
214
+ def from_request(
215
+ cls,
216
+ request: GlobalHypothesisAuditRequest,
217
+ *,
218
+ draft_hypothesis_sha256: str,
219
+ ) -> "CampaignSemanticAuditPlan":
220
+ if type(request) is not GlobalHypothesisAuditRequest:
221
+ raise TypeError("request must be an exact GlobalHypothesisAuditRequest")
222
+ GlobalHypothesisAuditRequest.__post_init__(request)
223
+ return cls(
224
+ reference=request.reference,
225
+ draft_content_sha256=request.draft_content_sha256,
226
+ draft_hypothesis_sha256=draft_hypothesis_sha256,
227
+ trigger=request.trigger,
228
+ intervention=request.intervention,
229
+ predictions=request.predictions,
230
+ claim_strength=request.claim_strength,
231
+ scope=request.scope,
232
+ matcher_definition_sha256=request.matcher_definition_sha256,
233
+ origin_cutoff_event_index=request.origin_cutoff_event_index,
234
+ minimum_support_clusters=request.minimum_support_clusters,
235
+ minimum_support_instances=request.minimum_support_instances,
236
+ audit_policy_definition_sha256=(request.audit_policy_definition_sha256),
237
+ )
238
+
239
+ def _unsigned_record(self) -> dict[str, object]:
240
+ return {
241
+ "schema_version": 1,
242
+ "reference": _reference_record(self.reference),
243
+ "draft_content_sha256": self.draft_content_sha256,
244
+ "draft_hypothesis_sha256": self.draft_hypothesis_sha256,
245
+ "trigger_predicate_sha256": self.trigger.predicate_sha256,
246
+ "intervention_signature_sha256": self.intervention.signature_sha256,
247
+ "predictions": [value.to_record() for value in self.predictions],
248
+ "claim_strength": self.claim_strength.to_record(),
249
+ "scope_sha256": self.scope.scope_sha256,
250
+ "matcher_definition_sha256": self.matcher_definition_sha256,
251
+ "origin_cutoff_event_index": self.origin_cutoff_event_index,
252
+ "minimum_support_clusters": self.minimum_support_clusters,
253
+ "minimum_support_instances": self.minimum_support_instances,
254
+ "audit_policy_definition_sha256": (self.audit_policy_definition_sha256),
255
+ }
256
+
257
+ def to_record(self) -> dict[str, object]:
258
+ self.__post_init__()
259
+ return {**self._unsigned_record(), "plan_sha256": self.plan_sha256}
260
+
261
+ def admits(self, request: GlobalHypothesisAuditRequest) -> bool:
262
+ """Whether a later sealed request preserves every planned semantic field."""
263
+
264
+ if type(request) is not GlobalHypothesisAuditRequest:
265
+ return False
266
+ GlobalHypothesisAuditRequest.__post_init__(request)
267
+ observed = type(self).from_request(
268
+ request,
269
+ draft_hypothesis_sha256=self.draft_hypothesis_sha256,
270
+ )
271
+ return observed.plan_sha256 == self.plan_sha256
272
+
273
+
274
+ @dataclass(frozen=True, slots=True)
275
+ class CampaignRandomizedPromotionEvidence:
276
+ """Exact robustness evidence for one randomized insight contrast.
277
+
278
+ This is intentionally separate from ``MarginalEffectEstimate``. The
279
+ latter remains useful for scoring, while this receipt records the stronger
280
+ evidence required to make a quarantined card normally retrievable.
281
+ """
282
+
283
+ reference: InsightRef
284
+ exact_context_sha256: str
285
+ source_trial_set_sha256: str
286
+ promotion_policy_definition_sha256: str
287
+ practical_effect_margin: float
288
+ independent_block_unit: str
289
+ block_effects: tuple[tuple[int, float], ...]
290
+ excluded_block_generations: tuple[int, ...]
291
+ missing_generation_trial_count: int
292
+ successful_block_count: int
293
+ exact_one_sided_p_value: Fraction
294
+ family_hypothesis_count: int
295
+ adjusted_alpha: float
296
+ failure_reasons: tuple[str, ...]
297
+ evidence_sha256: str = field(init=False)
298
+
299
+ def __post_init__(self) -> None:
300
+ if type(self.reference) is not InsightRef:
301
+ raise TypeError("reference must be an exact InsightRef")
302
+ InsightRef.__post_init__(self.reference)
303
+ require_sha256(self.exact_context_sha256, "exact_context_sha256")
304
+ require_sha256(self.source_trial_set_sha256, "source_trial_set_sha256")
305
+ require_sha256(
306
+ self.promotion_policy_definition_sha256,
307
+ "promotion_policy_definition_sha256",
308
+ )
309
+ for name in ("practical_effect_margin", "adjusted_alpha"):
310
+ value = getattr(self, name)
311
+ if type(value) is not float or not math.isfinite(value):
312
+ raise TypeError(f"{name} must be a finite canonical float")
313
+ if self.practical_effect_margin < 0:
314
+ raise ValueError("practical_effect_margin must be non-negative")
315
+ if not 0 < self.adjusted_alpha <= 0.5:
316
+ raise ValueError("adjusted_alpha must lie in (0, 0.5]")
317
+ if self.independent_block_unit != "campaign_generation":
318
+ raise ValueError("unsupported independent_block_unit")
319
+ if type(self.block_effects) is not tuple:
320
+ raise TypeError("block_effects must be an exact tuple")
321
+ generations: list[int] = []
322
+ effects: list[float] = []
323
+ for item in self.block_effects:
324
+ if type(item) is not tuple or len(item) != 2:
325
+ raise TypeError("block effects must be generation/effect pairs")
326
+ generation, effect = item
327
+ if type(generation) is not int or generation <= 0:
328
+ raise ValueError("block generation must be positive")
329
+ if type(effect) is not float or not math.isfinite(effect):
330
+ raise TypeError("block effect must be a finite canonical float")
331
+ generations.append(generation)
332
+ effects.append(effect)
333
+ if tuple(generations) != tuple(sorted(set(generations))):
334
+ raise ValueError("block generations must be unique and canonical")
335
+ if (
336
+ type(self.excluded_block_generations) is not tuple
337
+ or self.excluded_block_generations
338
+ != tuple(sorted(set(self.excluded_block_generations)))
339
+ or set(self.excluded_block_generations).intersection(generations)
340
+ ):
341
+ raise ValueError(
342
+ "excluded block generations must be disjoint and canonical"
343
+ )
344
+ for value in self.excluded_block_generations:
345
+ if type(value) is not int or value <= 0:
346
+ raise ValueError("excluded block generation must be positive")
347
+ if (
348
+ type(self.missing_generation_trial_count) is not int
349
+ or self.missing_generation_trial_count < 0
350
+ ):
351
+ raise ValueError("missing_generation_trial_count must be non-negative")
352
+ expected_successes = sum(
353
+ value > self.practical_effect_margin for value in effects
354
+ )
355
+ if self.successful_block_count != expected_successes:
356
+ raise ValueError("successful_block_count differs from block effects")
357
+ if type(self.exact_one_sided_p_value) is not Fraction:
358
+ raise TypeError("exact_one_sided_p_value must be an exact Fraction")
359
+ expected_p_value = _exact_one_sided_sign_p_value(
360
+ successes=self.successful_block_count,
361
+ blocks=len(self.block_effects),
362
+ )
363
+ if self.exact_one_sided_p_value != expected_p_value:
364
+ raise ValueError("exact sign p-value differs from block effects")
365
+ if (
366
+ type(self.family_hypothesis_count) is not int
367
+ or self.family_hypothesis_count <= 0
368
+ ):
369
+ raise ValueError("family_hypothesis_count must be positive")
370
+ if type(self.failure_reasons) is not tuple or any(
371
+ type(value) is not str or not value for value in self.failure_reasons
372
+ ):
373
+ raise TypeError("failure_reasons must contain non-empty exact strings")
374
+ if self.failure_reasons != tuple(dict.fromkeys(self.failure_reasons)):
375
+ raise ValueError("failure_reasons must be unique and canonical")
376
+ object.__setattr__(
377
+ self,
378
+ "evidence_sha256",
379
+ _hash(_PROMOTION_EVIDENCE_DOMAIN, self._unsigned_record()),
380
+ )
381
+
382
+ @property
383
+ def passes_policy(self) -> bool:
384
+ return not self.failure_reasons
385
+
386
+ def _unsigned_record(self) -> dict[str, object]:
387
+ return {
388
+ "schema_version": 1,
389
+ "reference": _reference_record(self.reference),
390
+ "exact_context_sha256": self.exact_context_sha256,
391
+ "source_trial_set_sha256": self.source_trial_set_sha256,
392
+ "promotion_policy_definition_sha256": (
393
+ self.promotion_policy_definition_sha256
394
+ ),
395
+ "practical_effect_margin_hex": self.practical_effect_margin.hex(),
396
+ "independent_block_unit": self.independent_block_unit,
397
+ "block_effects": [
398
+ {"generation": generation, "effect_hex": effect.hex()}
399
+ for generation, effect in self.block_effects
400
+ ],
401
+ "excluded_block_generations": list(self.excluded_block_generations),
402
+ "missing_generation_trial_count": self.missing_generation_trial_count,
403
+ "successful_block_count": self.successful_block_count,
404
+ "exact_one_sided_p_value": {
405
+ "numerator": self.exact_one_sided_p_value.numerator,
406
+ "denominator": self.exact_one_sided_p_value.denominator,
407
+ },
408
+ "family_hypothesis_count": self.family_hypothesis_count,
409
+ "adjusted_alpha_hex": self.adjusted_alpha.hex(),
410
+ "failure_reasons": list(self.failure_reasons),
411
+ "passes_policy": self.passes_policy,
412
+ }
413
+
414
+ def to_record(self) -> dict[str, object]:
415
+ self.__post_init__()
416
+ return {**self._unsigned_record(), "evidence_sha256": self.evidence_sha256}
417
+
418
+
419
+ @dataclass(frozen=True, slots=True)
420
+ class CampaignInsightPromotionPolicy:
421
+ """Predeclared conservative evidence for normal insight retrieval.
422
+
423
+ Promotion is deliberately harder than estimating a marginal effect. The
424
+ pooled stabilized-IPW contrast must clear a practical margin, both arms
425
+ must have adequate support, and that margin must recur across distinct
426
+ campaign-generation randomization blocks. The latter conjunct uses an
427
+ exact one-sided sign test and Bonferroni family-wise error control.
428
+
429
+ ``generation`` is the narrowest authenticated block identifier carried by
430
+ every production ``InsightTrial`` today. Trials without that identifier
431
+ can still update diagnostic scores, but they cannot promote a card.
432
+ """
433
+
434
+ minimum_treated_trials: int = 5
435
+ minimum_control_trials: int = 5
436
+ minimum_effective_support: float = 4.0
437
+ minimum_effect: float = 0.01
438
+ minimum_independent_blocks: int = 5
439
+ minimum_treated_trials_per_block: int = 1
440
+ minimum_control_trials_per_block: int = 1
441
+ familywise_error_rate: float = 0.05
442
+ independent_block_unit: str = "campaign_generation"
443
+ evidence_rule: str = "exact_one_sided_block_sign_test"
444
+ multiplicity_correction: str = "bonferroni"
445
+ policy_id: str = "semantic_support_and_robust_randomized_usefulness"
446
+ policy_version: int = 2
447
+
448
+ def __post_init__(self) -> None:
449
+ for name in (
450
+ "minimum_treated_trials",
451
+ "minimum_control_trials",
452
+ "minimum_independent_blocks",
453
+ "minimum_treated_trials_per_block",
454
+ "minimum_control_trials_per_block",
455
+ ):
456
+ value = getattr(self, name)
457
+ if type(value) is not int or value <= 0:
458
+ raise ValueError(f"{name} must be a positive exact integer")
459
+ for name in (
460
+ "minimum_effective_support",
461
+ "minimum_effect",
462
+ "familywise_error_rate",
463
+ ):
464
+ value = getattr(self, name)
465
+ if type(value) is not float or not math.isfinite(value):
466
+ raise TypeError(f"{name} must be a finite canonical float")
467
+ if self.minimum_effective_support <= 0:
468
+ raise ValueError("minimum_effective_support must be positive")
469
+ if self.minimum_effect < 0:
470
+ raise ValueError("minimum_effect must be non-negative")
471
+ if not 0 < self.familywise_error_rate <= 0.5:
472
+ raise ValueError("familywise_error_rate must lie in (0, 0.5]")
473
+ if self.independent_block_unit != "campaign_generation":
474
+ raise ValueError("unsupported independent_block_unit")
475
+ if self.evidence_rule != "exact_one_sided_block_sign_test":
476
+ raise ValueError("unsupported promotion evidence_rule")
477
+ if self.multiplicity_correction != "bonferroni":
478
+ raise ValueError("unsupported multiplicity_correction")
479
+ if self.policy_id != "semantic_support_and_robust_randomized_usefulness":
480
+ raise ValueError("unsupported promotion policy_id")
481
+ if type(self.policy_version) is not int or self.policy_version != 2:
482
+ raise ValueError("unsupported promotion policy_version")
483
+
484
+ def to_record(self) -> dict[str, object]:
485
+ self.__post_init__()
486
+ return {
487
+ "policy_id": self.policy_id,
488
+ "policy_version": self.policy_version,
489
+ "minimum_treated_trials": self.minimum_treated_trials,
490
+ "minimum_control_trials": self.minimum_control_trials,
491
+ "minimum_effective_support_hex": self.minimum_effective_support.hex(),
492
+ "minimum_effect_hex": self.minimum_effect.hex(),
493
+ "minimum_independent_blocks": self.minimum_independent_blocks,
494
+ "minimum_treated_trials_per_block": (self.minimum_treated_trials_per_block),
495
+ "minimum_control_trials_per_block": (self.minimum_control_trials_per_block),
496
+ "familywise_error_rate_hex": self.familywise_error_rate.hex(),
497
+ "independent_block_unit": self.independent_block_unit,
498
+ "evidence_rule": self.evidence_rule,
499
+ "multiplicity_correction": self.multiplicity_correction,
500
+ "effect_estimand": (
501
+ "stabilized_ipw_selected_minus_unselected_portfolio_reward"
502
+ ),
503
+ "block_effect_estimand": (
504
+ "within_generation_stabilized_ipw_selected_minus_unselected_reward"
505
+ ),
506
+ "tie_handling": "effect_equal_to_margin_is_not_success",
507
+ }
508
+
509
+ @property
510
+ def definition_sha256(self) -> str:
511
+ return _hash(_POLICY_DOMAIN, self.to_record())
512
+
513
+ def evaluate(
514
+ self,
515
+ *,
516
+ estimate: MarginalEffectEstimate,
517
+ source_trials: tuple[InsightTrial, ...],
518
+ source_trial_set_sha256: str,
519
+ family_hypothesis_count: int,
520
+ ) -> CampaignRandomizedPromotionEvidence:
521
+ """Build exact, replayable promotion evidence for one insight."""
522
+
523
+ self.__post_init__()
524
+ if type(estimate) is not MarginalEffectEstimate:
525
+ raise TypeError("estimate must be an exact MarginalEffectEstimate")
526
+ if estimate.context_hash is None:
527
+ raise ValueError("promotion requires an exact context stratum")
528
+ if type(source_trials) is not tuple or any(
529
+ type(value) is not InsightTrial for value in source_trials
530
+ ):
531
+ raise TypeError("source_trials must contain exact InsightTrial values")
532
+ for value in source_trials:
533
+ InsightTrial.__post_init__(value)
534
+ require_sha256(source_trial_set_sha256, "source_trial_set_sha256")
535
+ if type(family_hypothesis_count) is not int or family_hypothesis_count <= 0:
536
+ raise ValueError("family_hypothesis_count must be positive")
537
+
538
+ replayed = estimate_marginal_effect(
539
+ source_trials,
540
+ estimate.insight,
541
+ context_hash=estimate.context_hash,
542
+ )
543
+ if replayed != estimate:
544
+ raise ValueError("promotion estimate differs from its source trials")
545
+
546
+ trials_by_generation: dict[int, list[InsightTrial]] = {}
547
+ missing_generation_trial_count = 0
548
+ for trial in source_trials:
549
+ if trial.generation is None:
550
+ missing_generation_trial_count += 1
551
+ else:
552
+ trials_by_generation.setdefault(trial.generation, []).append(trial)
553
+
554
+ block_effects: list[tuple[int, float]] = []
555
+ excluded_generations: list[int] = []
556
+ for generation in sorted(trials_by_generation):
557
+ block_estimate = estimate_marginal_effect(
558
+ tuple(trials_by_generation[generation]),
559
+ estimate.insight,
560
+ context_hash=estimate.context_hash,
561
+ )
562
+ if (
563
+ block_estimate.effect is None
564
+ or block_estimate.treated_trials < self.minimum_treated_trials_per_block
565
+ or block_estimate.control_trials < self.minimum_control_trials_per_block
566
+ ):
567
+ excluded_generations.append(generation)
568
+ continue
569
+ block_effects.append((generation, block_estimate.effect))
570
+
571
+ successes = sum(effect > self.minimum_effect for _, effect in block_effects)
572
+ p_value = _exact_one_sided_sign_p_value(
573
+ successes=successes,
574
+ blocks=len(block_effects),
575
+ )
576
+ adjusted_alpha = self.familywise_error_rate / family_hypothesis_count
577
+ failure_reasons = self._failure_reasons(
578
+ estimate=estimate,
579
+ block_count=len(block_effects),
580
+ missing_generation_trial_count=missing_generation_trial_count,
581
+ exact_one_sided_p_value=p_value,
582
+ adjusted_alpha=adjusted_alpha,
583
+ )
584
+ return CampaignRandomizedPromotionEvidence(
585
+ reference=estimate.insight,
586
+ exact_context_sha256=estimate.context_hash,
587
+ source_trial_set_sha256=source_trial_set_sha256,
588
+ promotion_policy_definition_sha256=self.definition_sha256,
589
+ practical_effect_margin=self.minimum_effect,
590
+ independent_block_unit=self.independent_block_unit,
591
+ block_effects=tuple(block_effects),
592
+ excluded_block_generations=tuple(excluded_generations),
593
+ missing_generation_trial_count=missing_generation_trial_count,
594
+ successful_block_count=successes,
595
+ exact_one_sided_p_value=p_value,
596
+ family_hypothesis_count=family_hypothesis_count,
597
+ adjusted_alpha=adjusted_alpha,
598
+ failure_reasons=failure_reasons,
599
+ )
600
+
601
+ def _failure_reasons(
602
+ self,
603
+ *,
604
+ estimate: MarginalEffectEstimate,
605
+ block_count: int,
606
+ missing_generation_trial_count: int,
607
+ exact_one_sided_p_value: Fraction,
608
+ adjusted_alpha: float,
609
+ ) -> tuple[str, ...]:
610
+ reasons: list[str] = []
611
+ if estimate.effect is None:
612
+ reasons.append("pooled_effect_not_identified")
613
+ elif estimate.effect <= self.minimum_effect:
614
+ reasons.append("pooled_effect_does_not_clear_practical_margin")
615
+ if estimate.treated_trials < self.minimum_treated_trials:
616
+ reasons.append("insufficient_treated_trials")
617
+ if estimate.control_trials < self.minimum_control_trials:
618
+ reasons.append("insufficient_control_trials")
619
+ if (
620
+ min(
621
+ estimate.treated_effective_sample_size,
622
+ estimate.control_effective_sample_size,
623
+ )
624
+ < self.minimum_effective_support
625
+ ):
626
+ reasons.append("insufficient_effective_support")
627
+ if missing_generation_trial_count:
628
+ reasons.append("missing_authenticated_generation_block")
629
+ if block_count < self.minimum_independent_blocks:
630
+ reasons.append("insufficient_independent_blocks")
631
+ if float(exact_one_sided_p_value) > adjusted_alpha:
632
+ reasons.append("exact_block_sign_evidence_exceeds_adjusted_alpha")
633
+ return tuple(reasons)
634
+
635
+ def admits(self, evidence: CampaignRandomizedPromotionEvidence) -> bool:
636
+ self.__post_init__()
637
+ if type(evidence) is not CampaignRandomizedPromotionEvidence:
638
+ raise TypeError(
639
+ "evidence must be exact CampaignRandomizedPromotionEvidence"
640
+ )
641
+ CampaignRandomizedPromotionEvidence.__post_init__(evidence)
642
+ if evidence.promotion_policy_definition_sha256 != self.definition_sha256:
643
+ raise ValueError("promotion evidence was built under another policy")
644
+ return evidence.passes_policy
645
+
646
+
647
+ @dataclass(frozen=True, slots=True)
648
+ class CampaignInsightRegistrationReceipt:
649
+ origin_generation: int
650
+ entries: tuple[tuple[InsightRef, str], ...]
651
+ semantic_audit_plans: tuple[tuple[InsightRef, str, str], ...]
652
+ memory_trial_count_at_registration: int
653
+ receipt_sha256: str = field(init=False)
654
+
655
+ def __post_init__(self) -> None:
656
+ if type(self.origin_generation) is not int or self.origin_generation <= 0:
657
+ raise ValueError("origin_generation must be positive")
658
+ if type(self.entries) is not tuple or not self.entries:
659
+ raise ValueError("registration entries must be a non-empty exact tuple")
660
+ references: list[InsightRef] = []
661
+ for item in self.entries:
662
+ if type(item) is not tuple or len(item) != 2:
663
+ raise TypeError("registration entries must be reference/hash pairs")
664
+ reference, content_sha256 = item
665
+ if type(reference) is not InsightRef:
666
+ raise TypeError("registration reference must be exact")
667
+ require_sha256(content_sha256, "draft_content_sha256")
668
+ references.append(reference)
669
+ if tuple(references) != tuple(sorted(set(references))):
670
+ raise ValueError("registration references must be unique and canonical")
671
+ if type(self.semantic_audit_plans) is not tuple:
672
+ raise TypeError("semantic_audit_plans must be an exact tuple")
673
+ planned_references: list[InsightRef] = []
674
+ for item in self.semantic_audit_plans:
675
+ if type(item) is not tuple or len(item) != 3:
676
+ raise TypeError(
677
+ "semantic audit plans must be reference/hypothesis/plan triples"
678
+ )
679
+ reference, hypothesis_sha256, plan_sha256 = item
680
+ if type(reference) is not InsightRef:
681
+ raise TypeError("semantic audit plan reference must be exact")
682
+ require_sha256(hypothesis_sha256, "draft_hypothesis_sha256")
683
+ require_sha256(plan_sha256, "semantic_audit_plan_sha256")
684
+ planned_references.append(reference)
685
+ if tuple(planned_references) != tuple(references):
686
+ raise ValueError(
687
+ "semantic audit plans must exactly cover registration references"
688
+ )
689
+ if (
690
+ type(self.memory_trial_count_at_registration) is not int
691
+ or self.memory_trial_count_at_registration < 0
692
+ ):
693
+ raise ValueError("memory trial count must be non-negative")
694
+ object.__setattr__(
695
+ self,
696
+ "receipt_sha256",
697
+ _hash(_REGISTRATION_DOMAIN, self._unsigned_record()),
698
+ )
699
+
700
+ def _unsigned_record(self) -> dict[str, object]:
701
+ return {
702
+ "schema_version": 2,
703
+ "origin_generation": self.origin_generation,
704
+ "entries": [
705
+ {
706
+ "reference": _reference_record(reference),
707
+ "draft_content_sha256": content_sha256,
708
+ }
709
+ for reference, content_sha256 in self.entries
710
+ ],
711
+ "semantic_audit_plans": [
712
+ {
713
+ "reference": _reference_record(reference),
714
+ "draft_hypothesis_sha256": hypothesis_sha256,
715
+ "semantic_audit_plan_sha256": plan_sha256,
716
+ }
717
+ for reference, hypothesis_sha256, plan_sha256 in (
718
+ self.semantic_audit_plans
719
+ )
720
+ ],
721
+ "memory_trial_count_at_registration": (
722
+ self.memory_trial_count_at_registration
723
+ ),
724
+ "lifecycle_state": "quarantined",
725
+ }
726
+
727
+ def to_record(self) -> dict[str, object]:
728
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
729
+
730
+
731
+ @dataclass(frozen=True, slots=True)
732
+ class CampaignDiagnosticAdmissionReceipt:
733
+ admission_generation: int
734
+ references: tuple[InsightRef, ...]
735
+ registration_receipt_sha256s: tuple[str, ...]
736
+ campaign_admission_request_sha256: str
737
+ memory_admission_receipt_sha256: str
738
+ operator_kind: str
739
+ editable_paths: tuple[str, ...]
740
+ memory_trial_count_cutoff: int
741
+ receipt_sha256: str = field(init=False)
742
+
743
+ def __post_init__(self) -> None:
744
+ if type(self.admission_generation) is not int or self.admission_generation <= 0:
745
+ raise ValueError("admission_generation must be positive")
746
+ if (
747
+ type(self.references) is not tuple
748
+ or not self.references
749
+ or any(type(value) is not InsightRef for value in self.references)
750
+ ):
751
+ raise ValueError("references must be a non-empty exact tuple")
752
+ if self.references != tuple(sorted(set(self.references))):
753
+ raise ValueError("references must be unique and canonical")
754
+ if (
755
+ self.registration_receipt_sha256s
756
+ != tuple(sorted(set(self.registration_receipt_sha256s)))
757
+ or not self.registration_receipt_sha256s
758
+ ):
759
+ raise ValueError("registration receipt hashes must be canonical")
760
+ for value in self.registration_receipt_sha256s:
761
+ require_sha256(value, "registration_receipt_sha256")
762
+ require_sha256(
763
+ self.campaign_admission_request_sha256,
764
+ "campaign_admission_request_sha256",
765
+ )
766
+ require_sha256(
767
+ self.memory_admission_receipt_sha256,
768
+ "memory_admission_receipt_sha256",
769
+ )
770
+ if type(self.operator_kind) is not str or not self.operator_kind:
771
+ raise ValueError("operator_kind must be non-empty")
772
+ if type(self.editable_paths) is not tuple:
773
+ raise TypeError("editable_paths must be an exact tuple")
774
+ if self.editable_paths != tuple(sorted(set(self.editable_paths))):
775
+ raise ValueError("editable_paths must be unique and canonical")
776
+ if (
777
+ type(self.memory_trial_count_cutoff) is not int
778
+ or self.memory_trial_count_cutoff < 0
779
+ ):
780
+ raise ValueError("memory trial cutoff must be non-negative")
781
+ object.__setattr__(
782
+ self,
783
+ "receipt_sha256",
784
+ _hash(_ADMISSION_DOMAIN, self._unsigned_record()),
785
+ )
786
+
787
+ def _unsigned_record(self) -> dict[str, object]:
788
+ return {
789
+ "schema_version": 1,
790
+ "admission_generation": self.admission_generation,
791
+ "references": [_reference_record(value) for value in self.references],
792
+ "registration_receipt_sha256s": list(self.registration_receipt_sha256s),
793
+ "campaign_admission_request_sha256": (
794
+ self.campaign_admission_request_sha256
795
+ ),
796
+ "memory_admission_receipt_sha256": (self.memory_admission_receipt_sha256),
797
+ "operator_kind": self.operator_kind,
798
+ "editable_paths": list(self.editable_paths),
799
+ "memory_trial_count_cutoff": self.memory_trial_count_cutoff,
800
+ "visibility": "diagnostic_only_not_normal_retrieval",
801
+ }
802
+
803
+ def to_record(self) -> dict[str, object]:
804
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
805
+
806
+
807
+ @dataclass(frozen=True, slots=True)
808
+ class CampaignInsightAuditBinding:
809
+ request: GlobalHypothesisAuditRequest
810
+ receipt: GlobalHypothesisAuditReceipt
811
+ exact_context_sha256: str
812
+
813
+ def __post_init__(self) -> None:
814
+ if type(self.request) is not GlobalHypothesisAuditRequest:
815
+ raise TypeError("request must be an exact GlobalHypothesisAuditRequest")
816
+ if type(self.receipt) is not GlobalHypothesisAuditReceipt:
817
+ raise TypeError("receipt must be an exact GlobalHypothesisAuditReceipt")
818
+ GlobalHypothesisAuditRequest.__post_init__(self.request)
819
+ GlobalHypothesisAuditReceipt.__post_init__(self.receipt)
820
+ if self.receipt.request_sha256 != self.request.request_sha256:
821
+ raise ValueError("global audit receipt belongs to a different request")
822
+ if (
823
+ self.receipt.registry_snapshot_sha256
824
+ != self.request.registry_snapshot_sha256
825
+ ):
826
+ raise ValueError("global audit receipt names a different registry")
827
+ if (
828
+ self.request.audit_policy_definition_sha256
829
+ != GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256
830
+ ):
831
+ raise ValueError("global audit request substituted the sealed policy")
832
+ expected_policy = (
833
+ GLOBAL_FALSIFICATION_POLICY_ID,
834
+ GLOBAL_FALSIFICATION_POLICY_VERSION,
835
+ GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256,
836
+ )
837
+ observed_policy = (
838
+ self.receipt.audit_policy_id,
839
+ self.receipt.audit_policy_version,
840
+ self.receipt.audit_policy_definition_sha256,
841
+ )
842
+ if observed_policy != expected_policy:
843
+ raise ValueError("global audit receipt substituted the sealed policy")
844
+ require_sha256(self.exact_context_sha256, "exact_context_sha256")
845
+
846
+
847
+ @dataclass(frozen=True, slots=True)
848
+ class CampaignCausalUsefulnessReceipt:
849
+ reference: InsightRef
850
+ exact_context_sha256: str
851
+ admission_generation: int
852
+ barrier_generation: int
853
+ admission_trial_count_cutoff: int
854
+ barrier_trial_count_cutoff: int
855
+ source_trial_set_sha256: str
856
+ estimate: MarginalEffectEstimate
857
+ randomized_promotion_evidence: CampaignRandomizedPromotionEvidence
858
+ receipt_sha256: str = field(init=False)
859
+
860
+ def __post_init__(self) -> None:
861
+ if type(self.reference) is not InsightRef:
862
+ raise TypeError("reference must be exact")
863
+ require_sha256(self.exact_context_sha256, "exact_context_sha256")
864
+ if (
865
+ type(self.admission_generation) is not int
866
+ or type(self.barrier_generation) is not int
867
+ or self.admission_generation <= 0
868
+ or self.barrier_generation <= self.admission_generation
869
+ ):
870
+ raise ValueError("causal evidence requires a later barrier generation")
871
+ if (
872
+ type(self.admission_trial_count_cutoff) is not int
873
+ or type(self.barrier_trial_count_cutoff) is not int
874
+ or self.admission_trial_count_cutoff < 0
875
+ or self.barrier_trial_count_cutoff < self.admission_trial_count_cutoff
876
+ ):
877
+ raise ValueError("causal trial cutoffs are invalid")
878
+ require_sha256(self.source_trial_set_sha256, "source_trial_set_sha256")
879
+ if type(self.estimate) is not MarginalEffectEstimate:
880
+ raise TypeError("estimate must be exact")
881
+ if (
882
+ self.estimate.insight != self.reference
883
+ or self.estimate.context_hash != self.exact_context_sha256
884
+ ):
885
+ raise ValueError("causal estimate differs from its insight/context")
886
+ if (
887
+ type(self.randomized_promotion_evidence)
888
+ is not CampaignRandomizedPromotionEvidence
889
+ ):
890
+ raise TypeError("randomized_promotion_evidence must be exact")
891
+ CampaignRandomizedPromotionEvidence.__post_init__(
892
+ self.randomized_promotion_evidence
893
+ )
894
+ evidence = self.randomized_promotion_evidence
895
+ if (
896
+ evidence.reference != self.reference
897
+ or evidence.exact_context_sha256 != self.exact_context_sha256
898
+ or evidence.source_trial_set_sha256 != self.source_trial_set_sha256
899
+ ):
900
+ raise ValueError("randomized promotion evidence differs from causal source")
901
+ object.__setattr__(
902
+ self,
903
+ "receipt_sha256",
904
+ _hash(_CAUSAL_DOMAIN, self._unsigned_record()),
905
+ )
906
+
907
+ def _unsigned_record(self) -> dict[str, object]:
908
+ estimate = self.estimate
909
+ return {
910
+ "schema_version": 2,
911
+ "reference": _reference_record(self.reference),
912
+ "exact_context_sha256": self.exact_context_sha256,
913
+ "admission_generation": self.admission_generation,
914
+ "barrier_generation": self.barrier_generation,
915
+ "admission_trial_count_cutoff": self.admission_trial_count_cutoff,
916
+ "barrier_trial_count_cutoff": self.barrier_trial_count_cutoff,
917
+ "source_trial_set_sha256": self.source_trial_set_sha256,
918
+ "effect_hex": None if estimate.effect is None else estimate.effect.hex(),
919
+ "treated_trials": estimate.treated_trials,
920
+ "control_trials": estimate.control_trials,
921
+ "treated_ess_hex": estimate.treated_effective_sample_size.hex(),
922
+ "control_ess_hex": estimate.control_effective_sample_size.hex(),
923
+ "eligible_trials": estimate.eligible_trials,
924
+ "overlap_trials": estimate.overlap_trials,
925
+ "estimand": "randomized_selected_minus_unselected_portfolio_reward",
926
+ "randomized_promotion_evidence": (
927
+ self.randomized_promotion_evidence.to_record()
928
+ ),
929
+ }
930
+
931
+ def to_record(self) -> dict[str, object]:
932
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
933
+
934
+
935
+ class CampaignInsightLifecycleDecision(str, Enum):
936
+ PROMOTE = "promote"
937
+ DEPRECATE = "deprecate"
938
+ RETAIN_QUARANTINE = "retain_quarantine"
939
+
940
+
941
+ @dataclass(frozen=True, slots=True)
942
+ class CampaignInsightDecisionReceipt:
943
+ reference: InsightRef
944
+ semantic_audit_request_sha256: str
945
+ semantic_audit_receipt_sha256: str
946
+ causal_usefulness: CampaignCausalUsefulnessReceipt
947
+ decision: CampaignInsightLifecycleDecision
948
+ reason: str
949
+ lifecycle_transition_sequence: int | None
950
+ receipt_sha256: str = field(init=False)
951
+
952
+ def __post_init__(self) -> None:
953
+ if type(self.reference) is not InsightRef:
954
+ raise TypeError("reference must be exact")
955
+ require_sha256(
956
+ self.semantic_audit_request_sha256,
957
+ "semantic_audit_request_sha256",
958
+ )
959
+ require_sha256(
960
+ self.semantic_audit_receipt_sha256,
961
+ "semantic_audit_receipt_sha256",
962
+ )
963
+ if type(self.causal_usefulness) is not CampaignCausalUsefulnessReceipt:
964
+ raise TypeError("causal_usefulness must be exact")
965
+ if self.causal_usefulness.reference != self.reference:
966
+ raise ValueError("causal usefulness belongs to another insight")
967
+ if type(self.decision) is not CampaignInsightLifecycleDecision:
968
+ raise TypeError("decision must be exact")
969
+ if type(self.reason) is not str or not self.reason.strip():
970
+ raise ValueError("reason must be non-empty")
971
+ transitioned = self.decision is not (
972
+ CampaignInsightLifecycleDecision.RETAIN_QUARANTINE
973
+ )
974
+ if transitioned != (self.lifecycle_transition_sequence is not None):
975
+ raise ValueError("lifecycle transition sequence differs from decision")
976
+ if self.lifecycle_transition_sequence is not None and (
977
+ type(self.lifecycle_transition_sequence) is not int
978
+ or self.lifecycle_transition_sequence <= 0
979
+ ):
980
+ raise ValueError("lifecycle transition sequence must be positive")
981
+ object.__setattr__(
982
+ self,
983
+ "receipt_sha256",
984
+ _hash(_DECISION_DOMAIN, self._unsigned_record()),
985
+ )
986
+
987
+ def _unsigned_record(self) -> dict[str, object]:
988
+ return {
989
+ "schema_version": 1,
990
+ "reference": _reference_record(self.reference),
991
+ "semantic_audit_request_sha256": self.semantic_audit_request_sha256,
992
+ "semantic_audit_receipt_sha256": self.semantic_audit_receipt_sha256,
993
+ "causal_usefulness_receipt_sha256": (self.causal_usefulness.receipt_sha256),
994
+ "decision": self.decision.value,
995
+ "reason": self.reason,
996
+ "lifecycle_transition_sequence": self.lifecycle_transition_sequence,
997
+ }
998
+
999
+ def to_record(self) -> dict[str, object]:
1000
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
1001
+
1002
+
1003
+ @dataclass(frozen=True, slots=True)
1004
+ class CampaignLearningBarrierReceipt:
1005
+ generation: int
1006
+ memory_credit_batch_receipt_sha256: str
1007
+ promotion_policy_definition_sha256: str
1008
+ decisions: tuple[CampaignInsightDecisionReceipt, ...]
1009
+ receipt_sha256: str = field(init=False)
1010
+
1011
+ def __post_init__(self) -> None:
1012
+ if type(self.generation) is not int or self.generation <= 0:
1013
+ raise ValueError("generation must be positive")
1014
+ require_sha256(
1015
+ self.memory_credit_batch_receipt_sha256,
1016
+ "memory_credit_batch_receipt_sha256",
1017
+ )
1018
+ require_sha256(
1019
+ self.promotion_policy_definition_sha256,
1020
+ "promotion_policy_definition_sha256",
1021
+ )
1022
+ if (
1023
+ type(self.decisions) is not tuple
1024
+ or not self.decisions
1025
+ or any(
1026
+ type(value) is not CampaignInsightDecisionReceipt
1027
+ for value in self.decisions
1028
+ )
1029
+ ):
1030
+ raise ValueError("decisions must be a non-empty exact tuple")
1031
+ references = tuple(value.reference for value in self.decisions)
1032
+ if references != tuple(sorted(set(references))):
1033
+ raise ValueError("decisions must use unique canonical insight order")
1034
+ object.__setattr__(
1035
+ self,
1036
+ "receipt_sha256",
1037
+ _hash(_BARRIER_DOMAIN, self._unsigned_record()),
1038
+ )
1039
+
1040
+ def _unsigned_record(self) -> dict[str, object]:
1041
+ return {
1042
+ "schema_version": 1,
1043
+ "generation": self.generation,
1044
+ "memory_credit_batch_receipt_sha256": (
1045
+ self.memory_credit_batch_receipt_sha256
1046
+ ),
1047
+ "promotion_policy_definition_sha256": (
1048
+ self.promotion_policy_definition_sha256
1049
+ ),
1050
+ "decisions": [value.to_record() for value in self.decisions],
1051
+ "publication_scope": "post_generation_barrier_atomic_lifecycle_batch",
1052
+ }
1053
+
1054
+ def to_record(self) -> dict[str, object]:
1055
+ return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
1056
+
1057
+
1058
+ @dataclass(frozen=True, slots=True)
1059
+ class CampaignPreparedLearningBarrier:
1060
+ """Pure, sealed lifecycle decision awaiting memory-credit publication.
1061
+
1062
+ ``batch_trials`` are the exact trials expected to become the memory suffix.
1063
+ Preparing this value does not mutate either the bank or coordinator. Commit
1064
+ succeeds only if that exact suffix was published and lifecycle state stayed
1065
+ unchanged, leaving a deterministic atomic lifecycle batch.
1066
+ """
1067
+
1068
+ memory_credit_batch: PortfolioMemoryCreditBatchReceipt
1069
+ audits: tuple[CampaignInsightAuditBinding, ...]
1070
+ batch_trials: tuple[InsightTrial, ...]
1071
+ lifecycle_requests: tuple[InsightLifecycleChangeRequest, ...]
1072
+ barrier_receipt: CampaignLearningBarrierReceipt
1073
+ memory_trial_count_at_prepare: int
1074
+ transition_count_at_prepare: int
1075
+ preparation_sha256: str = field(init=False)
1076
+
1077
+ def __post_init__(self) -> None:
1078
+ if type(self.memory_credit_batch) is not PortfolioMemoryCreditBatchReceipt:
1079
+ raise TypeError("memory_credit_batch must be exact")
1080
+ PortfolioMemoryCreditBatchReceipt.__post_init__(self.memory_credit_batch)
1081
+ if (
1082
+ type(self.audits) is not tuple
1083
+ or not self.audits
1084
+ or any(
1085
+ type(value) is not CampaignInsightAuditBinding for value in self.audits
1086
+ )
1087
+ ):
1088
+ raise ValueError("audits must contain exact audit bindings")
1089
+ for value in self.audits:
1090
+ CampaignInsightAuditBinding.__post_init__(value)
1091
+ audit_references = tuple(value.request.reference for value in self.audits)
1092
+ if audit_references != tuple(sorted(set(audit_references))):
1093
+ raise ValueError("audits must use unique canonical insight order")
1094
+ if (
1095
+ type(self.batch_trials) is not tuple
1096
+ or not self.batch_trials
1097
+ or any(type(value) is not InsightTrial for value in self.batch_trials)
1098
+ ):
1099
+ raise ValueError("batch_trials must contain exact trials")
1100
+ for value in self.batch_trials:
1101
+ InsightTrial.__post_init__(value)
1102
+ credit_ids = tuple(value.credit_unit_id.value for value in self.batch_trials)
1103
+ if credit_ids != tuple(sorted(set(credit_ids))):
1104
+ raise ValueError("batch_trials must use canonical unique credit order")
1105
+ if len(self.batch_trials) != len(self.memory_credit_batch.credits):
1106
+ raise ValueError("batch_trials differ from the memory credit batch")
1107
+ if type(self.lifecycle_requests) is not tuple or any(
1108
+ type(value) is not InsightLifecycleChangeRequest
1109
+ for value in self.lifecycle_requests
1110
+ ):
1111
+ raise TypeError("lifecycle_requests must contain exact requests")
1112
+ for value in self.lifecycle_requests:
1113
+ InsightLifecycleChangeRequest.__post_init__(value)
1114
+ lifecycle_references = tuple(
1115
+ value.reference for value in self.lifecycle_requests
1116
+ )
1117
+ if lifecycle_references != tuple(sorted(set(lifecycle_references))):
1118
+ raise ValueError("lifecycle requests must use canonical insight order")
1119
+ if type(self.barrier_receipt) is not CampaignLearningBarrierReceipt:
1120
+ raise TypeError("barrier_receipt must be exact")
1121
+ CampaignLearningBarrierReceipt.__post_init__(self.barrier_receipt)
1122
+ if (
1123
+ self.barrier_receipt.generation != self.memory_credit_batch.generation
1124
+ or self.barrier_receipt.memory_credit_batch_receipt_sha256
1125
+ != self.memory_credit_batch.receipt_sha256
1126
+ ):
1127
+ raise ValueError("prepared barrier differs from its memory credit batch")
1128
+ transitioned = tuple(
1129
+ value.reference
1130
+ for value in self.barrier_receipt.decisions
1131
+ if value.lifecycle_transition_sequence is not None
1132
+ )
1133
+ if transitioned != lifecycle_references:
1134
+ raise ValueError("prepared lifecycle requests differ from decisions")
1135
+ for name in (
1136
+ "memory_trial_count_at_prepare",
1137
+ "transition_count_at_prepare",
1138
+ ):
1139
+ value = getattr(self, name)
1140
+ if type(value) is not int or value < 0:
1141
+ raise ValueError(f"{name} must be a non-negative exact integer")
1142
+ if self.memory_trial_count_at_prepare not in {
1143
+ self.memory_credit_batch.memory_trial_count_before,
1144
+ self.memory_credit_batch.memory_trial_count_after,
1145
+ }:
1146
+ raise ValueError("prepare trial count is outside the batch boundary")
1147
+ object.__setattr__(
1148
+ self,
1149
+ "preparation_sha256",
1150
+ _hash(_PREPARED_BARRIER_DOMAIN, self._unsigned_record()),
1151
+ )
1152
+
1153
+ def _unsigned_record(self) -> dict[str, object]:
1154
+ return {
1155
+ "schema_version": 1,
1156
+ "memory_credit_batch_receipt_sha256": (
1157
+ self.memory_credit_batch.receipt_sha256
1158
+ ),
1159
+ "semantic_audit_request_sha256s": [
1160
+ value.request.request_sha256 for value in self.audits
1161
+ ],
1162
+ "semantic_audit_receipt_sha256s": [
1163
+ value.receipt.audit_receipt_sha256 for value in self.audits
1164
+ ],
1165
+ "batch_trials": [_trial_record(value) for value in self.batch_trials],
1166
+ "lifecycle_requests": [
1167
+ {
1168
+ "reference": _reference_record(value.reference),
1169
+ "new_state": value.new_state.value,
1170
+ "reason": value.reason,
1171
+ "supporting_evidence": list(value.supporting_evidence),
1172
+ }
1173
+ for value in self.lifecycle_requests
1174
+ ],
1175
+ "barrier_receipt_sha256": self.barrier_receipt.receipt_sha256,
1176
+ "memory_trial_count_at_prepare": self.memory_trial_count_at_prepare,
1177
+ "transition_count_at_prepare": self.transition_count_at_prepare,
1178
+ }
1179
+
1180
+ def to_record(self) -> dict[str, object]:
1181
+ self.__post_init__()
1182
+ return {
1183
+ **self._unsigned_record(),
1184
+ "preparation_sha256": self.preparation_sha256,
1185
+ }
1186
+
1187
+
1188
+ @dataclass(slots=True)
1189
+ class ClosedLoopCampaignLearning:
1190
+ """Stateful coordinator around one campaign-owned insight memory bank."""
1191
+
1192
+ memory: InsightMemoryBank
1193
+ promotion_policy: CampaignInsightPromotionPolicy = field(
1194
+ default_factory=CampaignInsightPromotionPolicy
1195
+ )
1196
+ _registrations: dict[InsightRef, CampaignInsightRegistrationReceipt] = field(
1197
+ init=False,
1198
+ default_factory=dict,
1199
+ )
1200
+ _admissions: dict[InsightRef, CampaignDiagnosticAdmissionReceipt] = field(
1201
+ init=False,
1202
+ default_factory=dict,
1203
+ )
1204
+ _audit_plans: dict[InsightRef, CampaignSemanticAuditPlan] = field(
1205
+ init=False,
1206
+ default_factory=dict,
1207
+ )
1208
+ _closed_generations: set[int] = field(init=False, default_factory=set)
1209
+
1210
+ def __post_init__(self) -> None:
1211
+ if type(self.memory) is not InsightMemoryBank:
1212
+ raise TypeError("memory must be an exact InsightMemoryBank")
1213
+ if type(self.promotion_policy) is not CampaignInsightPromotionPolicy:
1214
+ raise TypeError("promotion_policy must be exact")
1215
+ self.promotion_policy.__post_init__()
1216
+
1217
+ def register_quarantined_reflections(
1218
+ self,
1219
+ *,
1220
+ origin_generation: int,
1221
+ references: tuple[InsightRef, ...],
1222
+ semantic_audit_plans: tuple[CampaignSemanticAuditPlan, ...],
1223
+ ) -> CampaignInsightRegistrationReceipt:
1224
+ self.__post_init__()
1225
+ if type(origin_generation) is not int or origin_generation <= 0:
1226
+ raise ValueError("origin_generation must be positive")
1227
+ if type(references) is not tuple or not references:
1228
+ raise ValueError("references must be a non-empty exact tuple")
1229
+ canonical = tuple(sorted(references))
1230
+ if len(set(canonical)) != len(canonical):
1231
+ raise ValueError("references cannot contain duplicates")
1232
+ if any(value in self._registrations for value in canonical):
1233
+ raise ValueError("an insight was already registered")
1234
+ if type(semantic_audit_plans) is not tuple or any(
1235
+ type(value) is not CampaignSemanticAuditPlan
1236
+ for value in semantic_audit_plans
1237
+ ):
1238
+ raise TypeError(
1239
+ "semantic_audit_plans must contain exact CampaignSemanticAuditPlan values"
1240
+ )
1241
+ for value in semantic_audit_plans:
1242
+ CampaignSemanticAuditPlan.__post_init__(value)
1243
+ plans = tuple(sorted(semantic_audit_plans, key=lambda value: value.reference))
1244
+ if tuple(value.reference for value in plans) != canonical:
1245
+ raise ValueError(
1246
+ "semantic audit plans must exactly cover registration references"
1247
+ )
1248
+ entries = self.memory.entries_for(canonical)
1249
+ if any(
1250
+ entry.lifecycle_state is not InsightLifecycleState.QUARANTINED
1251
+ or entry.origin is not InsightOrigin.REFLECTION
1252
+ for entry in entries
1253
+ ):
1254
+ raise ValueError("registration requires reflection-origin quarantine cards")
1255
+ for entry, plan in zip(entries, plans, strict=True):
1256
+ self._validate_semantic_audit_plan(entry, plan)
1257
+ receipt = CampaignInsightRegistrationReceipt(
1258
+ origin_generation=origin_generation,
1259
+ entries=tuple(
1260
+ (entry.reference, entry.draft.content_sha256) for entry in entries
1261
+ ),
1262
+ semantic_audit_plans=tuple(
1263
+ (
1264
+ entry.reference,
1265
+ entry.draft.hypothesis_sha256,
1266
+ plan.plan_sha256,
1267
+ )
1268
+ for entry, plan in zip(entries, plans, strict=True)
1269
+ ),
1270
+ memory_trial_count_at_registration=len(self.memory.trials),
1271
+ )
1272
+ for reference, plan in zip(canonical, plans, strict=True):
1273
+ self._registrations[reference] = receipt
1274
+ self._audit_plans[reference] = plan
1275
+ return receipt
1276
+
1277
+ @staticmethod
1278
+ def _validate_semantic_audit_plan(
1279
+ entry: InsightMemoryEntry,
1280
+ plan: CampaignSemanticAuditPlan,
1281
+ ) -> None:
1282
+ """Join a pre-outcome typed audit plan to one exact actionable card."""
1283
+
1284
+ draft = entry.draft
1285
+ if not draft.has_semantic_contract or not draft.has_intervention_contract:
1286
+ raise ValueError(
1287
+ "closed-loop registration requires an actionable semantic-v3 card"
1288
+ )
1289
+ if draft.insight_kind not in {
1290
+ ReflectionInsightKind.EMPIRICAL_PREDICTIVE_RULE,
1291
+ ReflectionInsightKind.MECHANISTIC_CONJECTURE,
1292
+ }:
1293
+ raise ValueError(
1294
+ "closed-loop registration rejects heuristic or invariant card kinds"
1295
+ )
1296
+ if not entry.applicable_operator_kinds:
1297
+ raise ValueError(
1298
+ "closed-loop registration requires explicit operator applicability"
1299
+ )
1300
+ if draft.affected_paths != tuple(sorted(set(draft.affected_paths))):
1301
+ raise ValueError("closed-loop affected paths must be canonical")
1302
+ if any(
1303
+ value.direction is MetricEffectDirection.UNKNOWN
1304
+ for value in draft.effect_predictions
1305
+ ):
1306
+ raise ValueError(
1307
+ "closed-loop promotion requires known auditable metric predictions"
1308
+ )
1309
+ expected_predictions = tuple(
1310
+ (value.metric_id, value.direction, None, None)
1311
+ for value in draft.effect_predictions
1312
+ )
1313
+ observed_predictions = tuple(
1314
+ (
1315
+ value.metric_id,
1316
+ value.direction,
1317
+ value.minimum_delta,
1318
+ value.maximum_delta,
1319
+ )
1320
+ for value in plan.predictions
1321
+ )
1322
+ expected_strength = HypothesisClaimStrength(
1323
+ sufficiency=True,
1324
+ necessity=False,
1325
+ invariance=False,
1326
+ mechanistic_or_causal=(
1327
+ draft.insight_kind is ReflectionInsightKind.MECHANISTIC_CONJECTURE
1328
+ ),
1329
+ )
1330
+ expected = (
1331
+ entry.reference,
1332
+ draft.content_sha256,
1333
+ draft.hypothesis_sha256,
1334
+ draft.affected_paths,
1335
+ entry.applicable_operator_kinds,
1336
+ expected_predictions,
1337
+ expected_strength,
1338
+ )
1339
+ observed = (
1340
+ plan.reference,
1341
+ plan.draft_content_sha256,
1342
+ plan.draft_hypothesis_sha256,
1343
+ plan.intervention.affected_paths,
1344
+ plan.intervention.admissible_operator_families,
1345
+ observed_predictions,
1346
+ plan.claim_strength,
1347
+ )
1348
+ if observed != expected:
1349
+ raise ValueError(
1350
+ "semantic audit plan differs from the registered card hypothesis"
1351
+ )
1352
+
1353
+ def audit_plans_for(
1354
+ self,
1355
+ references: tuple[InsightRef, ...],
1356
+ ) -> tuple[CampaignSemanticAuditPlan, ...]:
1357
+ """Return revalidated pre-outcome plans in exact canonical order.
1358
+
1359
+ This narrow projection lets an outcome-audit adapter compile a later
1360
+ sealed request without reaching into coordinator state. It never
1361
+ sorts on the caller's behalf: order substitution is rejected so the
1362
+ returned tuple can be joined positionally to a canonical audit family.
1363
+ """
1364
+
1365
+ self.__post_init__()
1366
+ if type(references) is not tuple or not references:
1367
+ raise ValueError("references must be a non-empty exact tuple")
1368
+ if any(type(value) is not InsightRef for value in references):
1369
+ raise TypeError("references must contain exact InsightRef values")
1370
+ for value in references:
1371
+ InsightRef.__post_init__(value)
1372
+ if references != tuple(sorted(set(references))):
1373
+ raise ValueError("references must be unique and canonically ordered")
1374
+ try:
1375
+ plans = tuple(self._audit_plans[value] for value in references)
1376
+ except KeyError as error:
1377
+ raise ValueError(
1378
+ "reference has no registered semantic audit plan"
1379
+ ) from error
1380
+ entries = self.memory.entries_for(references)
1381
+ for entry, plan in zip(entries, plans, strict=True):
1382
+ CampaignSemanticAuditPlan.__post_init__(plan)
1383
+ self._validate_semantic_audit_plan(entry, plan)
1384
+ return plans
1385
+
1386
+ def admit_for_diagnostic_testing(
1387
+ self,
1388
+ *,
1389
+ admission_generation: int,
1390
+ references: tuple[InsightRef, ...],
1391
+ campaign_admission_request_sha256: str,
1392
+ operator_kind: str,
1393
+ editable_paths: tuple[str, ...] = (),
1394
+ ) -> CampaignDiagnosticAdmissionReceipt:
1395
+ self.__post_init__()
1396
+ if type(references) is not tuple or not references:
1397
+ raise ValueError("references must be a non-empty exact tuple")
1398
+ canonical = tuple(sorted(references))
1399
+ if len(set(canonical)) != len(canonical):
1400
+ raise ValueError("references cannot contain duplicates")
1401
+ if any(value not in self._registrations for value in canonical):
1402
+ raise ValueError("diagnostic admission names an unregistered insight")
1403
+ if any(value in self._admissions for value in canonical):
1404
+ raise ValueError("an insight was already admitted for diagnostics")
1405
+ if any(
1406
+ admission_generation < self._registrations[value].origin_generation
1407
+ for value in canonical
1408
+ ):
1409
+ raise ValueError("diagnostic admission precedes reflection origin")
1410
+ memory_admission: QuarantineTestAdmissionReceipt = (
1411
+ self.memory.admit_quarantine_test_assignment(
1412
+ canonical,
1413
+ operator_kind=operator_kind,
1414
+ source_admission_request_sha256=(campaign_admission_request_sha256),
1415
+ editable_paths=editable_paths or None,
1416
+ )
1417
+ )
1418
+ receipt = CampaignDiagnosticAdmissionReceipt(
1419
+ admission_generation=admission_generation,
1420
+ references=memory_admission.references,
1421
+ registration_receipt_sha256s=tuple(
1422
+ sorted(
1423
+ {
1424
+ self._registrations[value].receipt_sha256
1425
+ for value in memory_admission.references
1426
+ }
1427
+ )
1428
+ ),
1429
+ campaign_admission_request_sha256=(campaign_admission_request_sha256),
1430
+ memory_admission_receipt_sha256=(memory_admission.receipt_sha256),
1431
+ operator_kind=operator_kind,
1432
+ editable_paths=tuple(sorted(set(editable_paths))),
1433
+ memory_trial_count_cutoff=(memory_admission.memory_trial_count_cutoff),
1434
+ )
1435
+ for reference in memory_admission.references:
1436
+ self._admissions[reference] = receipt
1437
+ return receipt
1438
+
1439
+ def prepare_generation_close(
1440
+ self,
1441
+ *,
1442
+ memory_credit_batch: PortfolioMemoryCreditBatchReceipt,
1443
+ audits: tuple[CampaignInsightAuditBinding, ...],
1444
+ prospective_trials: tuple[InsightTrial, ...] = (),
1445
+ ) -> CampaignPreparedLearningBarrier:
1446
+ """Prepare all semantic/causal decisions without mutating campaign state."""
1447
+
1448
+ self.__post_init__()
1449
+ if type(memory_credit_batch) is not PortfolioMemoryCreditBatchReceipt:
1450
+ raise TypeError("memory_credit_batch must be exact")
1451
+ PortfolioMemoryCreditBatchReceipt.__post_init__(memory_credit_batch)
1452
+ generation = memory_credit_batch.generation
1453
+ if generation in self._closed_generations:
1454
+ raise ValueError("campaign learning generation was already closed")
1455
+ if type(prospective_trials) is not tuple or any(
1456
+ type(value) is not InsightTrial for value in prospective_trials
1457
+ ):
1458
+ raise TypeError("prospective_trials must contain exact InsightTrial values")
1459
+ for value in prospective_trials:
1460
+ InsightTrial.__post_init__(value)
1461
+ memory_trial_count = len(self.memory.trials)
1462
+ if memory_trial_count == memory_credit_batch.memory_trial_count_after:
1463
+ committed = self.memory.trials[
1464
+ memory_credit_batch.memory_trial_count_before : memory_credit_batch.memory_trial_count_after
1465
+ ]
1466
+ if prospective_trials and prospective_trials != committed:
1467
+ raise ValueError("prospective trials differ from committed memory")
1468
+ batch_trials = committed
1469
+ trial_universe = self.memory.trials
1470
+ elif memory_trial_count == memory_credit_batch.memory_trial_count_before:
1471
+ if not prospective_trials:
1472
+ raise ValueError(
1473
+ "precommit campaign learning requires prospective memory trials"
1474
+ )
1475
+ batch_trials = prospective_trials
1476
+ trial_universe = (*self.memory.trials, *prospective_trials)
1477
+ else:
1478
+ raise ValueError("memory bank is outside the sealed batch boundary")
1479
+ self._validate_memory_credit_batch_trials(
1480
+ memory_credit_batch,
1481
+ batch_trials,
1482
+ )
1483
+ if len(trial_universe) != memory_credit_batch.memory_trial_count_after:
1484
+ raise ValueError("prospective trials do not close the memory batch")
1485
+ if type(audits) is not tuple or not audits:
1486
+ raise ValueError("audits must be a non-empty exact tuple")
1487
+ if any(type(value) is not CampaignInsightAuditBinding for value in audits):
1488
+ raise TypeError("audits must contain exact bindings")
1489
+ for value in audits:
1490
+ CampaignInsightAuditBinding.__post_init__(value)
1491
+ canonical = tuple(sorted(audits, key=lambda value: value.request.reference))
1492
+ references = tuple(value.request.reference for value in canonical)
1493
+ if len(set(references)) != len(references):
1494
+ raise ValueError("audits cannot repeat an insight reference")
1495
+
1496
+ prepared: list[
1497
+ tuple[
1498
+ CampaignInsightAuditBinding,
1499
+ CampaignCausalUsefulnessReceipt,
1500
+ CampaignInsightLifecycleDecision,
1501
+ str,
1502
+ ]
1503
+ ] = []
1504
+ lifecycle_requests: list[InsightLifecycleChangeRequest] = []
1505
+ for binding in canonical:
1506
+ reference = binding.request.reference
1507
+ admission = self._admissions.get(reference)
1508
+ if admission is None:
1509
+ raise ValueError("audit names an insight not admitted for diagnostics")
1510
+ if generation <= admission.admission_generation:
1511
+ raise ValueError("same-generation diagnostic outcomes are ineligible")
1512
+ entry = self.memory.entries_for((reference,))[0]
1513
+ if entry.lifecycle_state is not InsightLifecycleState.QUARANTINED:
1514
+ raise ValueError("audit insight is no longer quarantined")
1515
+ if binding.request.draft_content_sha256 != entry.draft.content_sha256:
1516
+ raise ValueError("global audit is bound to different card content")
1517
+ plan = self._audit_plans.get(reference)
1518
+ if plan is None:
1519
+ raise ValueError("audit insight has no pre-outcome semantic audit plan")
1520
+ self._validate_semantic_audit_plan(entry, plan)
1521
+ if not plan.admits(binding.request):
1522
+ raise ValueError(
1523
+ "global audit request substituted the registered semantic plan"
1524
+ )
1525
+ if (
1526
+ binding.request.origin_cutoff_event_index
1527
+ >= binding.request.audit_cutoff_event_index
1528
+ ):
1529
+ raise ValueError("semantic replay lacks a post-origin audit interval")
1530
+ self._validate_semantic_verdict(binding)
1531
+
1532
+ source_trials = tuple(
1533
+ trial
1534
+ for trial in trial_universe[
1535
+ admission.memory_trial_count_cutoff : memory_credit_batch.memory_trial_count_after
1536
+ ]
1537
+ if trial.decision.context_hash == binding.exact_context_sha256
1538
+ and reference in trial.decision.eligible
1539
+ )
1540
+ estimate = estimate_marginal_effect(
1541
+ source_trials,
1542
+ reference,
1543
+ context_hash=binding.exact_context_sha256,
1544
+ )
1545
+ source_trial_set_sha256 = _hash(
1546
+ b"agent-evolve:campaign-causal-trial-set:v1\x00",
1547
+ [_trial_record(value) for value in source_trials],
1548
+ )
1549
+ randomized_promotion_evidence = self.promotion_policy.evaluate(
1550
+ estimate=estimate,
1551
+ source_trials=source_trials,
1552
+ source_trial_set_sha256=source_trial_set_sha256,
1553
+ family_hypothesis_count=len(canonical),
1554
+ )
1555
+ causal = CampaignCausalUsefulnessReceipt(
1556
+ reference=reference,
1557
+ exact_context_sha256=binding.exact_context_sha256,
1558
+ admission_generation=admission.admission_generation,
1559
+ barrier_generation=generation,
1560
+ admission_trial_count_cutoff=(admission.memory_trial_count_cutoff),
1561
+ barrier_trial_count_cutoff=(
1562
+ memory_credit_batch.memory_trial_count_after
1563
+ ),
1564
+ source_trial_set_sha256=source_trial_set_sha256,
1565
+ estimate=estimate,
1566
+ randomized_promotion_evidence=randomized_promotion_evidence,
1567
+ )
1568
+ verdict = binding.receipt.verdict
1569
+ if verdict is GlobalHypothesisVerdict.COUNTEREXAMPLE:
1570
+ decision = CampaignInsightLifecycleDecision.DEPRECATE
1571
+ reason = "global semantic audit found a counterexample"
1572
+ lifecycle_requests.append(
1573
+ InsightLifecycleChangeRequest(
1574
+ reference=reference,
1575
+ new_state=InsightLifecycleState.DEPRECATED,
1576
+ reason=reason,
1577
+ supporting_evidence=(binding.receipt.audit_receipt_sha256,),
1578
+ )
1579
+ )
1580
+ elif (
1581
+ verdict is GlobalHypothesisVerdict.SUPPORT
1582
+ and self.promotion_policy.admits(randomized_promotion_evidence)
1583
+ ):
1584
+ decision = CampaignInsightLifecycleDecision.PROMOTE
1585
+ reason = (
1586
+ "semantic replay, practical effect, and exact block robustness "
1587
+ "all passed"
1588
+ )
1589
+ lifecycle_requests.append(
1590
+ InsightLifecycleChangeRequest(
1591
+ reference=reference,
1592
+ new_state=InsightLifecycleState.PROMOTED,
1593
+ reason=reason,
1594
+ supporting_evidence=tuple(
1595
+ sorted(
1596
+ (
1597
+ binding.receipt.audit_receipt_sha256,
1598
+ causal.receipt_sha256,
1599
+ )
1600
+ )
1601
+ ),
1602
+ )
1603
+ )
1604
+ else:
1605
+ decision = CampaignInsightLifecycleDecision.RETAIN_QUARANTINE
1606
+ if verdict is GlobalHypothesisVerdict.SUPPORT:
1607
+ detail = ",".join(randomized_promotion_evidence.failure_reasons)
1608
+ reason = f"randomized promotion evidence insufficient: {detail}"
1609
+ else:
1610
+ reason = "semantic promotion evidence remains insufficient"
1611
+ prepared.append((binding, causal, decision, reason))
1612
+ transition_count_before = len(self.memory.transitions)
1613
+ canonical_lifecycle_requests = tuple(
1614
+ sorted(lifecycle_requests, key=lambda value: value.reference)
1615
+ )
1616
+ transition_by_reference = {
1617
+ value.reference: transition_count_before + index
1618
+ for index, value in enumerate(
1619
+ canonical_lifecycle_requests,
1620
+ start=1,
1621
+ )
1622
+ }
1623
+ decisions = tuple(
1624
+ CampaignInsightDecisionReceipt(
1625
+ reference=binding.request.reference,
1626
+ semantic_audit_request_sha256=binding.request.request_sha256,
1627
+ semantic_audit_receipt_sha256=(binding.receipt.audit_receipt_sha256),
1628
+ causal_usefulness=causal,
1629
+ decision=decision,
1630
+ reason=reason,
1631
+ lifecycle_transition_sequence=transition_by_reference.get(
1632
+ binding.request.reference
1633
+ ),
1634
+ )
1635
+ for binding, causal, decision, reason in prepared
1636
+ )
1637
+ receipt = CampaignLearningBarrierReceipt(
1638
+ generation=generation,
1639
+ memory_credit_batch_receipt_sha256=(memory_credit_batch.receipt_sha256),
1640
+ promotion_policy_definition_sha256=(
1641
+ self.promotion_policy.definition_sha256
1642
+ ),
1643
+ decisions=decisions,
1644
+ )
1645
+ return CampaignPreparedLearningBarrier(
1646
+ memory_credit_batch=memory_credit_batch,
1647
+ audits=canonical,
1648
+ batch_trials=batch_trials,
1649
+ lifecycle_requests=canonical_lifecycle_requests,
1650
+ barrier_receipt=receipt,
1651
+ memory_trial_count_at_prepare=memory_trial_count,
1652
+ transition_count_at_prepare=transition_count_before,
1653
+ )
1654
+
1655
+ def commit_generation_close(
1656
+ self,
1657
+ preparation: CampaignPreparedLearningBarrier,
1658
+ ) -> CampaignLearningBarrierReceipt:
1659
+ """Commit one pure preparation after its exact memory batch is published."""
1660
+
1661
+ self.__post_init__()
1662
+ if type(preparation) is not CampaignPreparedLearningBarrier:
1663
+ raise TypeError("preparation must be exact")
1664
+ CampaignPreparedLearningBarrier.__post_init__(preparation)
1665
+ generation = preparation.memory_credit_batch.generation
1666
+ if generation in self._closed_generations:
1667
+ raise ValueError("campaign learning generation was already closed")
1668
+ if (
1669
+ len(self.memory.trials)
1670
+ != preparation.memory_credit_batch.memory_trial_count_after
1671
+ ):
1672
+ raise ValueError("memory batch was not published before lifecycle commit")
1673
+ self._validate_memory_credit_batch(preparation.memory_credit_batch)
1674
+ committed_trials = self.memory.trials[
1675
+ preparation.memory_credit_batch.memory_trial_count_before : preparation.memory_credit_batch.memory_trial_count_after
1676
+ ]
1677
+ if committed_trials != preparation.batch_trials:
1678
+ raise ValueError("published memory trials differ from learning preparation")
1679
+ if len(self.memory.transitions) != preparation.transition_count_at_prepare:
1680
+ raise ValueError("lifecycle state changed after learning preparation")
1681
+ if any(
1682
+ self.memory.entries_for((value.reference,))[0].lifecycle_state
1683
+ is not InsightLifecycleState.QUARANTINED
1684
+ for value in preparation.lifecycle_requests
1685
+ ):
1686
+ raise ValueError("prepared lifecycle card changed before commit")
1687
+ if preparation.lifecycle_requests:
1688
+ self.memory.apply_lifecycle_batch(preparation.lifecycle_requests)
1689
+ self._closed_generations.add(generation)
1690
+ return preparation.barrier_receipt
1691
+
1692
+ def close_generation(
1693
+ self,
1694
+ *,
1695
+ memory_credit_batch: PortfolioMemoryCreditBatchReceipt,
1696
+ audits: tuple[CampaignInsightAuditBinding, ...],
1697
+ ) -> CampaignLearningBarrierReceipt:
1698
+ """Compatibility wrapper for an already-committed memory batch."""
1699
+
1700
+ preparation = self.prepare_generation_close(
1701
+ memory_credit_batch=memory_credit_batch,
1702
+ audits=audits,
1703
+ )
1704
+ return self.commit_generation_close(preparation)
1705
+
1706
+ @staticmethod
1707
+ def _validate_semantic_verdict(binding: CampaignInsightAuditBinding) -> None:
1708
+ request = binding.request
1709
+ receipt = binding.receipt
1710
+ if receipt.verdict is GlobalHypothesisVerdict.SUPPORT:
1711
+ post_origin_support = tuple(
1712
+ value
1713
+ for value in receipt.decisions
1714
+ if value.disposition is EvidenceDisposition.SUPPORT
1715
+ and value.post_origin_revision_evidence
1716
+ and value.event_index > request.origin_cutoff_event_index
1717
+ )
1718
+ post_origin_support_ids = tuple(
1719
+ value.source_evidence_id for value in post_origin_support
1720
+ )
1721
+ post_origin_clusters = {
1722
+ value.effective_cluster_sha256 for value in post_origin_support
1723
+ }
1724
+ post_origin_instances = {
1725
+ value.workload_instance_sha256 for value in post_origin_support
1726
+ }
1727
+ if (
1728
+ not receipt.support_ids
1729
+ or receipt.effective_support_cluster_count
1730
+ < request.minimum_support_clusters
1731
+ or receipt.support_instance_count < request.minimum_support_instances
1732
+ or receipt.counterexample_ids
1733
+ or receipt.necessity_contradicted
1734
+ ):
1735
+ raise ValueError(
1736
+ "support verdict lacks its declared evidence threshold"
1737
+ )
1738
+ if (
1739
+ not post_origin_support_ids
1740
+ or len(post_origin_clusters) < request.minimum_support_clusters
1741
+ or len(post_origin_instances) < request.minimum_support_instances
1742
+ ):
1743
+ raise ValueError(
1744
+ "support verdict lacks post-origin evidence thresholds"
1745
+ )
1746
+ elif receipt.verdict is GlobalHypothesisVerdict.COUNTEREXAMPLE:
1747
+ if not receipt.counterexample_ids and not receipt.necessity_contradicted:
1748
+ raise ValueError("counterexample verdict contains no counterexample")
1749
+
1750
+ def _validate_memory_credit_batch(
1751
+ self,
1752
+ batch: PortfolioMemoryCreditBatchReceipt,
1753
+ ) -> None:
1754
+ trials = self.memory.trials[
1755
+ batch.memory_trial_count_before : batch.memory_trial_count_after
1756
+ ]
1757
+ self._validate_memory_credit_batch_trials(batch, trials)
1758
+
1759
+ @staticmethod
1760
+ def _validate_memory_credit_batch_trials(
1761
+ batch: PortfolioMemoryCreditBatchReceipt,
1762
+ trials: tuple[InsightTrial, ...],
1763
+ ) -> None:
1764
+ if type(batch) is not PortfolioMemoryCreditBatchReceipt:
1765
+ raise TypeError("batch must be exact")
1766
+ PortfolioMemoryCreditBatchReceipt.__post_init__(batch)
1767
+ if type(trials) is not tuple or any(
1768
+ type(value) is not InsightTrial for value in trials
1769
+ ):
1770
+ raise TypeError("trials must contain exact InsightTrial values")
1771
+ for value in trials:
1772
+ InsightTrial.__post_init__(value)
1773
+ if len(trials) != len(batch.credits):
1774
+ raise ValueError("memory batch differs from its exact trial suffix")
1775
+ trial_by_credit = {value.credit_unit_id: value for value in trials}
1776
+ if len(trial_by_credit) != len(trials):
1777
+ raise ValueError("trial suffix repeats a credit unit")
1778
+ for credit in batch.credits:
1779
+ trial = trial_by_credit.get(credit.credit_unit_id)
1780
+ if trial is None:
1781
+ raise ValueError("memory batch credit is absent from the trial suffix")
1782
+ if (
1783
+ trial.generation != batch.generation
1784
+ or trial.candidate_ids != credit.candidate_ids
1785
+ or trial.reward_definition_hash != credit.aggregation_definition_sha256
1786
+ or insight_selection_decision_sha256(trial.decision)
1787
+ != credit.selection_decision_sha256
1788
+ or trial.decision.context_hash
1789
+ != credit.selection_decision_context_sha256
1790
+ or trial.reward != credit.reward
1791
+ or trial.treatment_binding_sha256 != credit.treatment_binding_sha256
1792
+ or trial.treatment_binding_sha256 is None
1793
+ ):
1794
+ raise ValueError(
1795
+ "memory batch credit differs from its exact committed trial"
1796
+ )
1797
+
1798
+
1799
+ __all__ = [
1800
+ "CampaignCausalUsefulnessReceipt",
1801
+ "CampaignDiagnosticAdmissionReceipt",
1802
+ "CampaignInsightAuditBinding",
1803
+ "CampaignInsightDecisionReceipt",
1804
+ "CampaignInsightLifecycleDecision",
1805
+ "CampaignInsightPromotionPolicy",
1806
+ "CampaignInsightRegistrationReceipt",
1807
+ "CampaignLearningBarrierReceipt",
1808
+ "CampaignPreparedLearningBarrier",
1809
+ "CampaignRandomizedPromotionEvidence",
1810
+ "CampaignSemanticAuditPlan",
1811
+ "ClosedLoopCampaignLearning",
1812
+ ]