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.
- agent_evolve/__init__.py +722 -0
- agent_evolve/agentic.py +2800 -0
- agent_evolve/api.py +767 -0
- agent_evolve/application/__init__.py +1580 -0
- agent_evolve/application/action_allocation.py +744 -0
- agent_evolve/application/action_allocation_frame.py +347 -0
- agent_evolve/application/action_allocation_frame_commit.py +185 -0
- agent_evolve/application/action_allocation_frame_commit_v3.py +184 -0
- agent_evolve/application/action_allocation_frame_v3.py +338 -0
- agent_evolve/application/action_archive_value.py +497 -0
- agent_evolve/application/action_evidence_consistency.py +455 -0
- agent_evolve/application/action_forecast_partitioning.py +1471 -0
- agent_evolve/application/action_metric_projection.py +211 -0
- agent_evolve/application/action_role_value.py +680 -0
- agent_evolve/application/action_score_authorities.py +363 -0
- agent_evolve/application/action_structural_signature.py +116 -0
- agent_evolve/application/action_target_realization.py +402 -0
- agent_evolve/application/agentic_evolution.py +7734 -0
- agent_evolve/application/agentic_portfolio_residual_expert.py +835 -0
- agent_evolve/application/anchor_residual_identification.py +463 -0
- agent_evolve/application/archive_conditioned_action_target.py +208 -0
- agent_evolve/application/artifact_journal.py +246 -0
- agent_evolve/application/artifact_replay.py +347 -0
- agent_evolve/application/budgeted_optimizer.py +1828 -0
- agent_evolve/application/calibrated_campaign.py +485 -0
- agent_evolve/application/calibrated_current_prefix_forecast_opportunity.py +322 -0
- agent_evolve/application/calibrated_positive_gain_opportunity.py +1581 -0
- agent_evolve/application/campaign_capacity_recourse.py +254 -0
- agent_evolve/application/campaign_contextual_outcomes.py +119 -0
- agent_evolve/application/campaign_diagnostic_blocks.py +930 -0
- agent_evolve/application/campaign_evidence_registry.py +262 -0
- agent_evolve/application/campaign_execution.py +2537 -0
- agent_evolve/application/campaign_generation_audit.py +942 -0
- agent_evolve/application/campaign_learning.py +1812 -0
- agent_evolve/application/campaign_learning_runtime.py +1977 -0
- agent_evolve/application/campaign_search_phase.py +227 -0
- agent_evolve/application/campaign_selector_context_extension.py +220 -0
- agent_evolve/application/campaign_variation_envelope.py +649 -0
- agent_evolve/application/campaign_variation_trace.py +451 -0
- agent_evolve/application/candidate_archive_consequence.py +128 -0
- agent_evolve/application/causal_opportunity_portfolio_gate.py +385 -0
- agent_evolve/application/composite_outcome_updater.py +145 -0
- agent_evolve/application/composition_portfolio_selection.py +363 -0
- agent_evolve/application/concurrent_stage.py +144 -0
- agent_evolve/application/contextual_action_allocation.py +181 -0
- agent_evolve/application/contextual_campaign_outcomes.py +267 -0
- agent_evolve/application/contextual_campaign_planning.py +1366 -0
- agent_evolve/application/contextual_delayed_credit.py +651 -0
- agent_evolve/application/contextual_search_controller.py +2374 -0
- agent_evolve/application/current_prefix_forecast_opportunity.py +714 -0
- agent_evolve/application/decision_metric_projection.py +112 -0
- agent_evolve/application/derived_action_semantics.py +129 -0
- agent_evolve/application/detailed_evaluation.py +449 -0
- agent_evolve/application/earned_lineage.py +1011 -0
- agent_evolve/application/effective_choice_audit.py +484 -0
- agent_evolve/application/empirical_consequence_calibration.py +908 -0
- agent_evolve/application/evaluation_accounting.py +325 -0
- agent_evolve/application/evaluation_cache.py +199 -0
- agent_evolve/application/evaluation_escrow.py +547 -0
- agent_evolve/application/evaluation_recourse.py +253 -0
- agent_evolve/application/event_recorder.py +151 -0
- agent_evolve/application/evolution_campaign.py +1840 -0
- agent_evolve/application/executable_hypothesis.py +323 -0
- agent_evolve/application/factorial_branch_pilot.py +772 -0
- agent_evolve/application/finite_acquisition_capacity_recourse.py +672 -0
- agent_evolve/application/finite_acquisition_residual_expert.py +373 -0
- agent_evolve/application/finite_acquisition_variation_envelope.py +802 -0
- agent_evolve/application/finite_action_hypothesis_semantics.py +446 -0
- agent_evolve/application/finite_action_selection.py +188 -0
- agent_evolve/application/finite_action_set.py +306 -0
- agent_evolve/application/finite_action_transition.py +537 -0
- agent_evolve/application/finite_variation_eligibility.py +296 -0
- agent_evolve/application/forecast_geometry_portfolio.py +799 -0
- agent_evolve/application/forecast_opportunity_shadow_calibration.py +316 -0
- agent_evolve/application/front_proximity_admission.py +311 -0
- agent_evolve/application/front_proximity_parent_basis.py +458 -0
- agent_evolve/application/frozen_hurdle_score.py +659 -0
- agent_evolve/application/g3_causal_screen.py +2257 -0
- agent_evolve/application/g3_causal_validation.py +1046 -0
- agent_evolve/application/g3_postseal_curation.py +818 -0
- agent_evolve/application/gated_agentic_generator.py +205 -0
- agent_evolve/application/generation_feedback.py +293 -0
- agent_evolve/application/generative_proposal_journal.py +185 -0
- agent_evolve/application/geometry_conditional_elasticity.py +453 -0
- agent_evolve/application/global_wave_action_allocation.py +1151 -0
- agent_evolve/application/head_mass_conditional_seat.py +268 -0
- agent_evolve/application/identifiable_reflection_evidence.py +1147 -0
- agent_evolve/application/identifiable_reflection_learning.py +395 -0
- agent_evolve/application/identifiable_reflection_request.py +364 -0
- agent_evolve/application/in_memory_residual_archive.py +341 -0
- agent_evolve/application/insight_memory.py +1804 -0
- agent_evolve/application/live_runtime_manifest.py +758 -0
- agent_evolve/application/llm_task_queue.py +769 -0
- agent_evolve/application/matched_finite_action_block.py +409 -0
- agent_evolve/application/materialized_action_broker.py +2328 -0
- agent_evolve/application/materialized_action_constraints.py +83 -0
- agent_evolve/application/materialized_variation.py +211 -0
- agent_evolve/application/multi_option_evolution.py +1536 -0
- agent_evolve/application/outcome_adaptive_action_racing.py +2827 -0
- agent_evolve/application/outcome_adaptive_residual_campaign_runtime.py +580 -0
- agent_evolve/application/outcome_adaptive_residual_portfolio_evolution.py +3671 -0
- agent_evolve/application/outcome_conditioned_portfolio_selection.py +1374 -0
- agent_evolve/application/outcome_relation.py +193 -0
- agent_evolve/application/paired_allocation_comparison.py +241 -0
- agent_evolve/application/paired_block_schedule.py +127 -0
- agent_evolve/application/parent_measurement.py +226 -0
- agent_evolve/application/pareto_archive.py +811 -0
- agent_evolve/application/portfolio_campaign_runtime.py +4739 -0
- agent_evolve/application/portfolio_evolution.py +2950 -0
- agent_evolve/application/portfolio_hypothesis_observations.py +814 -0
- agent_evolve/application/portfolio_memory_attribution.py +581 -0
- agent_evolve/application/portfolio_memory_dose.py +788 -0
- agent_evolve/application/portfolio_memory_matched_control.py +938 -0
- agent_evolve/application/portfolio_memory_transfer.py +297 -0
- agent_evolve/application/portfolio_optimization_memory.py +363 -0
- agent_evolve/application/portfolio_outcome_feedback.py +1613 -0
- agent_evolve/application/portfolio_projection.py +335 -0
- agent_evolve/application/portfolio_recombination.py +2032 -0
- agent_evolve/application/post_evolution_reflection.py +834 -0
- agent_evolve/application/postcommit_rank_authority.py +245 -0
- agent_evolve/application/precommitted_portfolio_racing.py +2762 -0
- agent_evolve/application/prequential_archive_opportunity_calibration.py +1154 -0
- agent_evolve/application/prequential_residual_exploration.py +343 -0
- agent_evolve/application/prequential_score_portfolio.py +954 -0
- agent_evolve/application/projections.py +292 -0
- agent_evolve/application/protected_action_committee.py +1027 -0
- agent_evolve/application/protected_branch_pilot.py +376 -0
- agent_evolve/application/protected_current_prefix_forecast_opportunity.py +552 -0
- agent_evolve/application/provider_replay.py +910 -0
- agent_evolve/application/rank_balanced_causal_pilot.py +1372 -0
- agent_evolve/application/recombination_residual_expert.py +403 -0
- agent_evolve/application/reflection_workflow.py +571 -0
- agent_evolve/application/region_conditional_credit.py +911 -0
- agent_evolve/application/residual_campaign_runtime.py +531 -0
- agent_evolve/application/residual_headroom_campaign_runtime.py +459 -0
- agent_evolve/application/residual_headroom_ledger.py +1544 -0
- agent_evolve/application/residual_learning_transaction.py +396 -0
- agent_evolve/application/residual_portfolio_evolution.py +1228 -0
- agent_evolve/application/residual_reachability.py +749 -0
- agent_evolve/application/residual_stage_credit.py +499 -0
- agent_evolve/application/same_prefix_paired_audit.py +1580 -0
- agent_evolve/application/semantic_coverage_score_portfolio.py +838 -0
- agent_evolve/application/sequential_lineage_allocation.py +1017 -0
- agent_evolve/application/sequential_market_replay.py +1395 -0
- agent_evolve/application/sequential_residual_campaign_runtime.py +305 -0
- agent_evolve/application/sequential_residual_portfolio_evolution.py +940 -0
- agent_evolve/application/single_score_action_allocation.py +299 -0
- agent_evolve/application/source_exposure_allocation.py +906 -0
- agent_evolve/application/staged_memory.py +210 -0
- agent_evolve/application/stratified_cold_start_allocation.py +732 -0
- agent_evolve/application/support_guarded_hurdle_score.py +549 -0
- agent_evolve/application/target_conditioned_action_forecast.py +595 -0
- agent_evolve/application/target_conditioned_campaign.py +566 -0
- agent_evolve/application/treatment_assignment.py +201 -0
- agent_evolve/application/trusted_objective_evidence.py +217 -0
- agent_evolve/application/two_stage_action_evolution.py +1131 -0
- agent_evolve/application/v8lite_allocation_policy.py +1083 -0
- agent_evolve/application/v9_candidate_policy.py +1303 -0
- agent_evolve/bootstrap.py +108 -0
- agent_evolve/campaign_presets.py +517 -0
- agent_evolve/campaign_profiles.py +452 -0
- agent_evolve/campaign_variation_topology.py +288 -0
- agent_evolve/campaign_workload.py +950 -0
- agent_evolve/cli.py +797 -0
- agent_evolve/contract.py +241 -0
- agent_evolve/core/__init__.py +91 -0
- agent_evolve/core/action_semantics.py +411 -0
- agent_evolve/core/authored.py +105 -0
- agent_evolve/core/formatting.py +286 -0
- agent_evolve/core/optimization_semantics.py +324 -0
- agent_evolve/core/problem.py +167 -0
- agent_evolve/core/results.py +323 -0
- agent_evolve/core/stats.py +70 -0
- agent_evolve/core/telemetry.py +100 -0
- agent_evolve/domain/__init__.py +89 -0
- agent_evolve/domain/artifact.py +162 -0
- agent_evolve/domain/durable_text.py +68 -0
- agent_evolve/domain/event.py +1454 -0
- agent_evolve/domain/finite_action_set.py +426 -0
- agent_evolve/domain/finite_variation.py +526 -0
- agent_evolve/domain/generative_emission.py +559 -0
- agent_evolve/domain/ids.py +163 -0
- agent_evolve/domain/inline_text.py +106 -0
- agent_evolve/domain/insight.py +27 -0
- agent_evolve/domain/lineage.py +737 -0
- agent_evolve/domain/llm_task_queue.py +960 -0
- agent_evolve/domain/outcome.py +96 -0
- agent_evolve/domain/patch.py +854 -0
- agent_evolve/domain/typed_json.py +542 -0
- agent_evolve/domain/variation_space.py +158 -0
- agent_evolve/driver.py +1014 -0
- agent_evolve/harness/__init__.py +29 -0
- agent_evolve/harness/base.py +242 -0
- agent_evolve/harness/directives.py +163 -0
- agent_evolve/harness/generative_seal.py +479 -0
- agent_evolve/harness/registry.py +41 -0
- agent_evolve/infrastructure/__init__.py +39 -0
- agent_evolve/infrastructure/artifacts/__init__.py +6 -0
- agent_evolve/infrastructure/artifacts/_verification.py +67 -0
- agent_evolve/infrastructure/artifacts/filesystem.py +343 -0
- agent_evolve/infrastructure/artifacts/in_memory.py +73 -0
- agent_evolve/infrastructure/asyncio_runtime.py +109 -0
- agent_evolve/infrastructure/authored_runtime.py +188 -0
- agent_evolve/infrastructure/authored_worker.py +171 -0
- agent_evolve/infrastructure/clock.py +53 -0
- agent_evolve/infrastructure/events/__init__.py +6 -0
- agent_evolve/infrastructure/events/_validation.py +89 -0
- agent_evolve/infrastructure/events/in_memory.py +56 -0
- agent_evolve/infrastructure/events/jsonl.py +193 -0
- agent_evolve/infrastructure/exception_provenance.py +215 -0
- agent_evolve/infrastructure/ids.py +118 -0
- agent_evolve/infrastructure/lineage_codec.py +1836 -0
- agent_evolve/infrastructure/outcome_adaptive_phase_journal.py +170 -0
- agent_evolve/infrastructure/residual_headroom_journal.py +221 -0
- agent_evolve/infrastructure/resource_lease.py +370 -0
- agent_evolve/infrastructure/sanitization/__init__.py +8 -0
- agent_evolve/infrastructure/sanitization/strict_json.py +484 -0
- agent_evolve/infrastructure/sequential_phase_journal.py +170 -0
- agent_evolve/infrastructure/stream_liveness.py +383 -0
- agent_evolve/infrastructure/subprocess_boundary.py +136 -0
- agent_evolve/integrations/__init__.py +1 -0
- agent_evolve/integrations/botorch/__init__.py +28 -0
- agent_evolve/integrations/botorch/finite_qlognehvi.py +190 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch.py +155 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_identity.py +20 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_worker.py +55 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_identity.py +22 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_worker.py +55 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi.py +261 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi_batch.py +273 -0
- agent_evolve/integrations/completion.py +242 -0
- agent_evolve/integrations/pydantic_ai/__init__.py +441 -0
- agent_evolve/integrations/pydantic_ai/action_forecast.py +1068 -0
- agent_evolve/integrations/pydantic_ai/agentic_generator.py +2308 -0
- agent_evolve/integrations/pydantic_ai/async_generator.py +1604 -0
- agent_evolve/integrations/pydantic_ai/boundary_codec.py +1526 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_campaign.py +756 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_selection.py +7537 -0
- agent_evolve/integrations/pydantic_ai/campaign_acquisition.py +609 -0
- agent_evolve/integrations/pydantic_ai/execution_binding.py +138 -0
- agent_evolve/integrations/pydantic_ai/forecast_geometry_action_committee.py +217 -0
- agent_evolve/integrations/pydantic_ai/harness.py +159 -0
- agent_evolve/integrations/pydantic_ai/heterogeneous_model_execution.py +306 -0
- agent_evolve/integrations/pydantic_ai/hierarchical_residual_adaptive_semantic_view.py +179 -0
- agent_evolve/integrations/pydantic_ai/json_schema_dialect.py +108 -0
- agent_evolve/integrations/pydantic_ai/materialized_hierarchical_residual_expert.py +952 -0
- agent_evolve/integrations/pydantic_ai/materialized_portfolio_judge.py +520 -0
- agent_evolve/integrations/pydantic_ai/model_execution_profile.py +659 -0
- agent_evolve/integrations/pydantic_ai/outbound_request_manifest.py +1170 -0
- agent_evolve/integrations/pydantic_ai/portable_residual_consequence_features.py +575 -0
- agent_evolve/integrations/pydantic_ai/portfolio_selection.py +422 -0
- agent_evolve/integrations/pydantic_ai/progress_aware_openrouter.py +416 -0
- agent_evolve/integrations/pydantic_ai/provider_attempt_join.py +1523 -0
- agent_evolve/integrations/pydantic_ai/provider_free_calibrated_runner.py +607 -0
- agent_evolve/integrations/pydantic_ai/queued_runner.py +2634 -0
- agent_evolve/integrations/pydantic_ai/reconciled_residual_reachability.py +1417 -0
- agent_evolve/integrations/pydantic_ai/residual_forecast_geometry.py +445 -0
- agent_evolve/integrations/pydantic_ai/residual_reachability.py +674 -0
- agent_evolve/integrations/pydantic_ai/residual_semantic_cells.py +239 -0
- agent_evolve/integrations/pydantic_ai/sealed_output_replay.py +1068 -0
- agent_evolve/integrations/pydantic_ai/semantic_coverage_residual_portfolio.py +770 -0
- agent_evolve/integrations/pydantic_ai/semantic_decision_replay.py +383 -0
- agent_evolve/integrations/pydantic_ai/support_adaptive_residual_portfolio.py +135 -0
- agent_evolve/integrations/pydantic_ai/trusted_residual_prompt_context.py +143 -0
- agent_evolve/integrations/pydantic_ai/validated_openrouter_model.py +107 -0
- agent_evolve/integrations/pymoo_adapter.py +242 -0
- agent_evolve/policies/__init__.py +17 -0
- agent_evolve/policies/check.py +469 -0
- agent_evolve/policies/emit_scaffold.py +451 -0
- agent_evolve/policies/feedback/__init__.py +37 -0
- agent_evolve/policies/feedback/held_out_asn.py +1325 -0
- agent_evolve/policies/genetic.py +607 -0
- agent_evolve/policies/llm_backoff.py +183 -0
- agent_evolve/policies/llm_chooser.py +226 -0
- agent_evolve/policies/llm_generator.py +1760 -0
- agent_evolve/policies/llm_init.py +267 -0
- agent_evolve/policies/llm_operator.py +109 -0
- agent_evolve/policies/llm_prior.py +194 -0
- agent_evolve/policies/llm_surrogate.py +334 -0
- agent_evolve/policies/measurement_evidence.py +704 -0
- agent_evolve/policies/memory/__init__.py +223 -0
- agent_evolve/policies/memory/balanced_subset_blocks.py +707 -0
- agent_evolve/policies/memory/compatibility_matching.py +593 -0
- agent_evolve/policies/memory/global_falsification.py +1841 -0
- agent_evolve/policies/memory/prompt_shape.py +503 -0
- agent_evolve/policies/memory/randomized_subset.py +714 -0
- agent_evolve/policies/memory/staged_causal.py +1270 -0
- agent_evolve/policies/memory/treatment_compliance.py +759 -0
- agent_evolve/policies/objective_resolution/__init__.py +17 -0
- agent_evolve/policies/objective_resolution/fixed_grid.py +364 -0
- agent_evolve/policies/operator_portfolio.py +407 -0
- agent_evolve/policies/reguidance.py +1133 -0
- agent_evolve/policies/reward/__init__.py +83 -0
- agent_evolve/policies/reward/affine_candidate_consequence.py +156 -0
- agent_evolve/policies/reward/affine_candidate_consequence_3d.py +159 -0
- agent_evolve/policies/reward/affine_hypervolume.py +490 -0
- agent_evolve/policies/reward/affine_hypervolume_3d.py +567 -0
- agent_evolve/policies/reward/contextual_marginal_utility.py +318 -0
- agent_evolve/policies/reward/frozen_archive.py +360 -0
- agent_evolve/policies/reward/frozen_wave_archive.py +368 -0
- agent_evolve/policies/search_state.py +208 -0
- agent_evolve/policies/selection/__init__.py +345 -0
- agent_evolve/policies/selection/acquisition_certified_slate.py +684 -0
- agent_evolve/policies/selection/affine_frontier_context.py +330 -0
- agent_evolve/policies/selection/affine_frontier_target.py +473 -0
- agent_evolve/policies/selection/archive_elite.py +1346 -0
- agent_evolve/policies/selection/calibrated_portfolio_binding.py +640 -0
- agent_evolve/policies/selection/calibrated_slate.py +1394 -0
- agent_evolve/policies/selection/calibrated_slate_codec.py +579 -0
- agent_evolve/policies/selection/common_candidate_pool.py +685 -0
- agent_evolve/policies/selection/diagnostic_sampling.py +319 -0
- agent_evolve/policies/selection/disjoint_pairs.py +479 -0
- agent_evolve/policies/selection/elite_explorer.py +719 -0
- agent_evolve/policies/selection/finite_action.py +187 -0
- agent_evolve/policies/selection/finite_option_prompt_projection.py +377 -0
- agent_evolve/policies/selection/finite_palette_evidence.py +247 -0
- agent_evolve/policies/selection/forecast_calibration.py +922 -0
- agent_evolve/policies/selection/frontier_probe_slate.py +814 -0
- agent_evolve/policies/selection/frozen_archive_pairs.py +762 -0
- agent_evolve/policies/selection/full_support_slate.py +91 -0
- agent_evolve/policies/selection/meaningful_direction.py +240 -0
- agent_evolve/policies/selection/memory_dose_feasibility.py +259 -0
- agent_evolve/policies/selection/model_anchored_slate.py +826 -0
- agent_evolve/policies/selection/phenotype_recourse.py +979 -0
- agent_evolve/policies/selection/proposal_support.py +368 -0
- agent_evolve/policies/selection/random_portfolio.py +254 -0
- agent_evolve/policies/selection/regret_bounded_slate.py +1084 -0
- agent_evolve/policies/selection/residual_frontier.py +463 -0
- agent_evolve/policies/selection/residual_frontier_target.py +605 -0
- agent_evolve/policies/selection/structural_posterior_slate.py +1571 -0
- agent_evolve/policies/selection/target_conditioned_allocator.py +648 -0
- agent_evolve/policies/selection/target_conditioned_features.py +812 -0
- agent_evolve/policies/selection/target_conditioned_prequential.py +1527 -0
- agent_evolve/policies/selection/task_keyed_palette.py +906 -0
- agent_evolve/policies/semantics.py +147 -0
- agent_evolve/policies/structure.py +362 -0
- agent_evolve/policies/structured_output_budget.py +62 -0
- agent_evolve/policies/surrogate.py +696 -0
- agent_evolve/policies/variation/__init__.py +1 -0
- agent_evolve/policies/variation/compositional_finite_catalog.py +426 -0
- agent_evolve/policies/variation/crossover_inheritance.py +575 -0
- agent_evolve/policies/variation/disjoint_recombination.py +611 -0
- agent_evolve/policies/variation/exact_composition_capacity.py +214 -0
- agent_evolve/policies/variation/exact_parent_crossover.py +950 -0
- agent_evolve/policies/variation/multiscale_restart_catalog.py +372 -0
- agent_evolve/policies/variation/source_union_finite_catalog.py +403 -0
- agent_evolve/policies/variation/typed_patch.py +1981 -0
- agent_evolve/policies/weighted_prior.py +394 -0
- agent_evolve/ports/__init__.py +383 -0
- agent_evolve/ports/action_allocation.py +733 -0
- agent_evolve/ports/action_allocation_frame.py +1153 -0
- agent_evolve/ports/action_allocation_frame_commit.py +294 -0
- agent_evolve/ports/action_allocation_frame_commit_v3.py +432 -0
- agent_evolve/ports/action_allocation_frame_v3.py +995 -0
- agent_evolve/ports/action_forecast.py +1568 -0
- agent_evolve/ports/action_metric_projection.py +165 -0
- agent_evolve/ports/agentic_generator.py +1561 -0
- agent_evolve/ports/archive_context.py +136 -0
- agent_evolve/ports/artifact_sanitizer.py +44 -0
- agent_evolve/ports/artifact_store.py +225 -0
- agent_evolve/ports/clock.py +13 -0
- agent_evolve/ports/contextual_search_allocation.py +827 -0
- agent_evolve/ports/decision_metric_projection.py +258 -0
- agent_evolve/ports/event_store.py +55 -0
- agent_evolve/ports/executable_hypothesis.py +557 -0
- agent_evolve/ports/finite_acquisition.py +377 -0
- agent_evolve/ports/finite_acquisition_batch.py +296 -0
- agent_evolve/ports/finite_acquisition_batch_json.py +164 -0
- agent_evolve/ports/finite_acquisition_json.py +247 -0
- agent_evolve/ports/finite_acquisition_space.py +168 -0
- agent_evolve/ports/finite_action_selection.py +348 -0
- agent_evolve/ports/finite_action_set.py +256 -0
- agent_evolve/ports/frontier_target.py +396 -0
- agent_evolve/ports/generation_failure.py +43 -0
- agent_evolve/ports/hard_feasibility.py +233 -0
- agent_evolve/ports/id_factory.py +34 -0
- agent_evolve/ports/llm_task_queue.py +93 -0
- agent_evolve/ports/objective_resolution.py +419 -0
- agent_evolve/ports/paired_allocation_comparison.py +401 -0
- agent_evolve/ports/paired_block_schedule.py +475 -0
- agent_evolve/ports/parent_measurement.py +336 -0
- agent_evolve/ports/portfolio_memory_dose.py +643 -0
- agent_evolve/ports/portfolio_selection.py +3169 -0
- agent_evolve/ports/postcommit_rank_authority.py +467 -0
- agent_evolve/ports/presented_action_evidence.py +794 -0
- agent_evolve/ports/resource_lease.py +162 -0
- agent_evolve/ports/structured_generator.py +734 -0
- agent_evolve/ports/structured_output_budget.py +120 -0
- agent_evolve/ports/subprocess_boundary.py +138 -0
- agent_evolve/ports/treatment_assignment.py +466 -0
- agent_evolve/ports/variation_catalog.py +76 -0
- agent_evolve/ports/variation_source.py +226 -0
- agent_evolve/proposal_mode.py +157 -0
- agent_evolve/proposers/__init__.py +10 -0
- agent_evolve/proposers/random_proposer.py +188 -0
- agent_evolve/provider_accounting.py +163 -0
- agent_evolve/py.typed +0 -0
- agent_evolve/reference_method.py +1570 -0
- agent_evolve/session/__init__.py +11 -0
- agent_evolve/session/authorship.py +864 -0
- agent_evolve/session/evaluate.py +236 -0
- agent_evolve/session/fidelity.py +237 -0
- agent_evolve/session/genetic_loop.py +742 -0
- agent_evolve/session/loop.py +803 -0
- agent_evolve/session/screening.py +671 -0
- agent_evolve/settings.py +376 -0
- agent_evolve/workload_kit.py +368 -0
- agent_evolve/workload_prompt.py +398 -0
- agentevolve_optimizer-0.5.0.dist-info/METADATA +599 -0
- agentevolve_optimizer-0.5.0.dist-info/RECORD +414 -0
- agentevolve_optimizer-0.5.0.dist-info/WHEEL +5 -0
- agentevolve_optimizer-0.5.0.dist-info/entry_points.txt +2 -0
- agentevolve_optimizer-0.5.0.dist-info/licenses/LICENSE +21 -0
- agentevolve_optimizer-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,4739 @@
|
|
|
1
|
+
"""Concrete execution bridge for prepared portfolio-evolution campaigns.
|
|
2
|
+
|
|
3
|
+
The preparation API deliberately knows nothing about mutable optimizer state.
|
|
4
|
+
This adapter supplies that missing runtime: it evaluates the prepared seeds,
|
|
5
|
+
maintains one benchmark-bound Pareto archive, binds each parent through the
|
|
6
|
+
prepared :class:`CampaignWorkloadPorts`, executes real
|
|
7
|
+
:class:`PortfolioEvolution` and :class:`PortfolioRecombination` waves, and
|
|
8
|
+
projects their evidence into :class:`EvolutionCampaignScheduler` receipts.
|
|
9
|
+
|
|
10
|
+
Injected ports keep each experimental choice replaceable: parent selection,
|
|
11
|
+
portfolio-wave construction, parent-local context retrieval, post-generation
|
|
12
|
+
outcome updates, reflection execution, closed-loop learning, recombination
|
|
13
|
+
utility, prompt rendering, and owned-resource cleanup. The runtime owns only
|
|
14
|
+
ordering, validation, and evidence publication across those boundaries.
|
|
15
|
+
|
|
16
|
+
No workload, objective name, model, or provider is named in this module.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import hashlib
|
|
22
|
+
import json
|
|
23
|
+
import math
|
|
24
|
+
import re
|
|
25
|
+
from dataclasses import dataclass, field, replace
|
|
26
|
+
from enum import Enum
|
|
27
|
+
from typing import Protocol, runtime_checkable
|
|
28
|
+
|
|
29
|
+
from agent_evolve.agentic import PhenotypeIdentity, PortfolioEvolutionComposition
|
|
30
|
+
from agent_evolve.application.agentic_evolution import EvolutionCandidate
|
|
31
|
+
from agent_evolve.application.budgeted_optimizer import (
|
|
32
|
+
OptimizerState,
|
|
33
|
+
pareto_archive_snapshot_hash,
|
|
34
|
+
)
|
|
35
|
+
from agent_evolve.application.campaign_execution import (
|
|
36
|
+
CampaignArchiveCutoffReceipt,
|
|
37
|
+
CampaignArchiveCutoffRequest,
|
|
38
|
+
CampaignCleanupReceipt,
|
|
39
|
+
CampaignCleanupRequest,
|
|
40
|
+
CampaignExecutionStatus,
|
|
41
|
+
CampaignExecutionStartReceipt,
|
|
42
|
+
CampaignFinalizationReceipt,
|
|
43
|
+
CampaignFinalizationRequest,
|
|
44
|
+
CampaignReflectionReceipt,
|
|
45
|
+
CampaignReflectionRequest,
|
|
46
|
+
CampaignReflectionStatus,
|
|
47
|
+
CampaignReflectionTestAdmissionReceipt,
|
|
48
|
+
CampaignReflectionTestAdmissionRequest,
|
|
49
|
+
CampaignSeedExecutionReceipt,
|
|
50
|
+
CampaignSelectorAuditReceipt,
|
|
51
|
+
CampaignStageReceipt,
|
|
52
|
+
CampaignStageRequest,
|
|
53
|
+
SelectorAuditExecutionMode,
|
|
54
|
+
encode_selector_audit_text,
|
|
55
|
+
)
|
|
56
|
+
from agent_evolve.application.campaign_capacity_recourse import (
|
|
57
|
+
CampaignCapacityRecoursePort,
|
|
58
|
+
CampaignCapacityRecourseRequest,
|
|
59
|
+
validate_campaign_capacity_recourse_port,
|
|
60
|
+
validate_campaign_capacity_recourse_result,
|
|
61
|
+
)
|
|
62
|
+
from agent_evolve.application.concurrent_stage import gather_concurrent_stage
|
|
63
|
+
from agent_evolve.application.contextual_campaign_planning import (
|
|
64
|
+
CampaignContextualSearchPlanner,
|
|
65
|
+
)
|
|
66
|
+
from agent_evolve.application.contextual_delayed_credit import (
|
|
67
|
+
observe_contextual_post_recombination_credit,
|
|
68
|
+
observe_contextual_post_stage_survival,
|
|
69
|
+
observe_contextual_terminal_persistence,
|
|
70
|
+
)
|
|
71
|
+
from agent_evolve.application.evolution_campaign import (
|
|
72
|
+
ArchiveUtilitySnapshot,
|
|
73
|
+
CampaignGenerationKind,
|
|
74
|
+
CampaignReflectionWave,
|
|
75
|
+
CampaignWorkloadPorts,
|
|
76
|
+
ParentVariationBinding,
|
|
77
|
+
PreparedEvolutionCampaign,
|
|
78
|
+
ReflectionVisibility,
|
|
79
|
+
)
|
|
80
|
+
from agent_evolve.application.campaign_evidence_registry import (
|
|
81
|
+
CampaignEvidenceRegistry,
|
|
82
|
+
)
|
|
83
|
+
from agent_evolve.application.campaign_selector_context_extension import (
|
|
84
|
+
CAMPAIGN_SELECTOR_CONTEXT_EXTENSION_KEY,
|
|
85
|
+
resolve_campaign_selector_context_extension,
|
|
86
|
+
)
|
|
87
|
+
from agent_evolve.application.campaign_search_phase import (
|
|
88
|
+
attach_campaign_search_phase_context,
|
|
89
|
+
campaign_search_phase_context,
|
|
90
|
+
)
|
|
91
|
+
from agent_evolve.application.campaign_variation_envelope import (
|
|
92
|
+
CampaignVariationEnvelopeLane,
|
|
93
|
+
CampaignVariationEnvelopePolicy,
|
|
94
|
+
CampaignVariationEnvelopeRequest,
|
|
95
|
+
campaign_variation_envelope_context_record,
|
|
96
|
+
campaign_variation_envelope_trace_record,
|
|
97
|
+
validate_campaign_variation_envelope_result,
|
|
98
|
+
)
|
|
99
|
+
from agent_evolve.application.identifiable_reflection_evidence import (
|
|
100
|
+
IdentifiableReflectionEvidenceSnapshot,
|
|
101
|
+
NoIdentifiableMutationEvidenceError,
|
|
102
|
+
ReflectionFalsificationFeedback,
|
|
103
|
+
project_identifiable_reflection_evidence,
|
|
104
|
+
)
|
|
105
|
+
from agent_evolve.application.pareto_archive import ParetoArchive, ParetoDecision
|
|
106
|
+
from agent_evolve.application.parent_measurement import (
|
|
107
|
+
attach_parent_measurement_to_context,
|
|
108
|
+
bind_parent_measurement,
|
|
109
|
+
)
|
|
110
|
+
from agent_evolve.application.portfolio_evolution import (
|
|
111
|
+
MEMORY_ESTIMAND_CONTEXT_KEY,
|
|
112
|
+
PortfolioMemoryCreditBatchPreparation,
|
|
113
|
+
PortfolioMemoryContextProjectionBinding,
|
|
114
|
+
PortfolioVariationWaveRequest,
|
|
115
|
+
PortfolioVariationWaveResult,
|
|
116
|
+
PortfolioMemberDisposition,
|
|
117
|
+
)
|
|
118
|
+
from agent_evolve.application.portfolio_recombination import (
|
|
119
|
+
PortfolioRecombination,
|
|
120
|
+
PortfolioRecombinationWaveRequest,
|
|
121
|
+
PortfolioRecombinationWaveResult,
|
|
122
|
+
)
|
|
123
|
+
from agent_evolve.campaign_workload import AgenticCampaignWorkloadConfig
|
|
124
|
+
from agent_evolve.domain.patch import require_sha256
|
|
125
|
+
from agent_evolve.domain.typed_json import (
|
|
126
|
+
FrozenJsonObject,
|
|
127
|
+
freeze_json,
|
|
128
|
+
thaw_json,
|
|
129
|
+
typed_json_sha256,
|
|
130
|
+
)
|
|
131
|
+
from agent_evolve.policies.variation.typed_patch import derive_patch
|
|
132
|
+
from agent_evolve.policies.memory.global_falsification import (
|
|
133
|
+
AuthenticatedHypothesisObservation,
|
|
134
|
+
GlobalEvidenceRegistrySnapshot,
|
|
135
|
+
)
|
|
136
|
+
from agent_evolve.integrations.pydantic_ai.portfolio_selection import (
|
|
137
|
+
render_portfolio_selection_prompt,
|
|
138
|
+
)
|
|
139
|
+
from agent_evolve.policies.selection.archive_elite import (
|
|
140
|
+
TaskKeyedArchiveEliteParentPolicy,
|
|
141
|
+
TaskKeyedArchiveReservoirParentPolicy,
|
|
142
|
+
)
|
|
143
|
+
from agent_evolve.policies.selection.elite_explorer import (
|
|
144
|
+
TaskKeyedArchiveEliteExplorerParentPolicy,
|
|
145
|
+
)
|
|
146
|
+
from agent_evolve.policies.selection.frozen_archive_pairs import (
|
|
147
|
+
FrozenArchiveSourceUtilityReceipt,
|
|
148
|
+
)
|
|
149
|
+
from agent_evolve.policies.selection.residual_frontier import (
|
|
150
|
+
RESIDUAL_FRONTIER_POLICY_DEFINITION_SHA256,
|
|
151
|
+
residual_anchor_parents,
|
|
152
|
+
residual_frontier_geometry,
|
|
153
|
+
)
|
|
154
|
+
from agent_evolve.ports.portfolio_selection import PortfolioSelectionRequest
|
|
155
|
+
from agent_evolve.ports.contextual_search_allocation import (
|
|
156
|
+
ContextualPortfolioAllocationContract,
|
|
157
|
+
)
|
|
158
|
+
from agent_evolve.ports.frontier_target import (
|
|
159
|
+
CampaignPortfolioFrontierTarget,
|
|
160
|
+
CampaignPortfolioFrontierTargetAllocator,
|
|
161
|
+
)
|
|
162
|
+
from agent_evolve.ports.objective_resolution import (
|
|
163
|
+
EXACT_OBJECTIVE_RESOLUTION_DEFINITION_SHA256,
|
|
164
|
+
EXACT_OBJECTIVE_RESOLUTION_POLICY_ID,
|
|
165
|
+
EXACT_OBJECTIVE_RESOLUTION_POLICY_VERSION,
|
|
166
|
+
objective_resolution_policy_metadata,
|
|
167
|
+
)
|
|
168
|
+
from agent_evolve.ports.parent_measurement import (
|
|
169
|
+
ParentMeasurementBinding,
|
|
170
|
+
ParentMeasurementProjection,
|
|
171
|
+
)
|
|
172
|
+
from agent_evolve.ports.archive_context import (
|
|
173
|
+
CampaignPortfolioArchiveContextProjection,
|
|
174
|
+
CampaignPortfolioArchiveContextProjector,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _object(value: dict[str, object]) -> FrozenJsonObject:
|
|
179
|
+
frozen = freeze_json(value)
|
|
180
|
+
if type(frozen) is not FrozenJsonObject: # pragma: no cover - closed input.
|
|
181
|
+
raise AssertionError("runtime record did not freeze to an object")
|
|
182
|
+
return frozen
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _canonical_text(value: object) -> str:
|
|
186
|
+
return json.dumps(
|
|
187
|
+
value,
|
|
188
|
+
allow_nan=False,
|
|
189
|
+
ensure_ascii=True,
|
|
190
|
+
separators=(",", ":"),
|
|
191
|
+
sort_keys=True,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
_CAMPAIGN_ROLE_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
196
|
+
CAMPAIGN_CONTEXTUAL_HISTORY_KEY = "campaign_contextual_history"
|
|
197
|
+
CAMPAIGN_ARCHIVE_CONTEXT_KEY = "campaign_archive_context"
|
|
198
|
+
CAMPAIGN_FRONTIER_TARGET_KEY = "campaign_frontier_target"
|
|
199
|
+
CAMPAIGN_VARIATION_ENVELOPE_KEY = "campaign_variation_envelope"
|
|
200
|
+
CAMPAIGN_IDENTIFIABLE_REFLECTION_BINDING_KEY = (
|
|
201
|
+
"campaign_identifiable_reflection_binding"
|
|
202
|
+
)
|
|
203
|
+
MEMORY_ESTIMAND_STRATUM_SHA256_KEY = "memory_estimand_stratum_sha256"
|
|
204
|
+
_LOWER_SHA256 = frozenset("0123456789abcdef")
|
|
205
|
+
_OUTCOME_PREPARATION_DOMAIN = (
|
|
206
|
+
b"agent-evolve:campaign-portfolio-outcome-preparation:v1\x00"
|
|
207
|
+
)
|
|
208
|
+
_LEARNING_PREPARATION_DOMAIN = (
|
|
209
|
+
b"agent-evolve:campaign-portfolio-learning-preparation:v1\x00"
|
|
210
|
+
)
|
|
211
|
+
_WAVE_PREPARATION_DOMAIN = b"agent-evolve:campaign-portfolio-wave-preparation:v1\x00"
|
|
212
|
+
_IDENTIFIABLE_REFLECTION_QUERY_DOMAIN = (
|
|
213
|
+
b"agent-evolve:campaign-identifiable-reflection-query:v1\x00"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class RecombinationEvaluationAllocationMode(str, Enum):
|
|
218
|
+
"""Choose which generic proposal port receives recombination capacity.
|
|
219
|
+
|
|
220
|
+
``NATIVE_THEN_RECOURSE`` preserves the production behavior: evaluate the
|
|
221
|
+
replay-safe native recombination slate and ask the injected recourse port
|
|
222
|
+
only to fill unused occurrences. ``RECOURSE_ONLY`` is an explicit
|
|
223
|
+
prospective ablation that gives the complete stage budget to the recourse
|
|
224
|
+
specialist. Neither mode names a workload, objective, provider, or
|
|
225
|
+
concrete proposal algorithm.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
NATIVE_THEN_RECOURSE = "native_then_recourse"
|
|
229
|
+
RECOURSE_ONLY = "recourse_only"
|
|
230
|
+
_IDENTIFIABLE_REFLECTION_SOURCE_DOMAIN = (
|
|
231
|
+
b"agent-evolve:campaign-identifiable-reflection-source:v1\x00"
|
|
232
|
+
)
|
|
233
|
+
_IDENTIFIABLE_REFLECTION_INPUT_DOMAIN = (
|
|
234
|
+
b"agent-evolve:campaign-identifiable-reflection-input:v1\x00"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _require_role_token(value: str, name: str) -> None:
|
|
239
|
+
if type(value) is not str or _CAMPAIGN_ROLE_TOKEN.fullmatch(value) is None:
|
|
240
|
+
raise ValueError(f"{name} must use the closed lowercase token grammar")
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _require_sha256(value: str, name: str) -> None:
|
|
244
|
+
if (
|
|
245
|
+
type(value) is not str
|
|
246
|
+
or len(value) != 64
|
|
247
|
+
or any(character not in _LOWER_SHA256 for character in value)
|
|
248
|
+
):
|
|
249
|
+
raise ValueError(f"{name} must be a lowercase SHA-256 digest")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _preparation_sha256(domain: bytes, record: FrozenJsonObject) -> str:
|
|
253
|
+
return hashlib.sha256(
|
|
254
|
+
domain + _canonical_text(thaw_json(record)).encode("ascii", errors="strict")
|
|
255
|
+
).hexdigest()
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _candidate_record(candidate: EvolutionCandidate) -> dict[str, object]:
|
|
259
|
+
EvolutionCandidate.__post_init__(candidate)
|
|
260
|
+
record: dict[str, object] = {
|
|
261
|
+
"candidate_id": candidate.candidate_id.value,
|
|
262
|
+
"configuration_sha256": candidate.occurrence.configuration_hash,
|
|
263
|
+
"generation": candidate.generation,
|
|
264
|
+
"label": candidate.label,
|
|
265
|
+
"valid": candidate.valid,
|
|
266
|
+
"operator_compliant": candidate.operator_compliant,
|
|
267
|
+
"evidence_compliant": candidate.evidence_compliant,
|
|
268
|
+
"objectives": [
|
|
269
|
+
{"metric_id": name, "value_hex": value.hex()}
|
|
270
|
+
for name, value in candidate.objectives
|
|
271
|
+
],
|
|
272
|
+
"operator_kind": (
|
|
273
|
+
None if candidate.operator_kind is None else candidate.operator_kind.value
|
|
274
|
+
),
|
|
275
|
+
"parent_ids": [value.value for value in candidate.parent_ids],
|
|
276
|
+
"common_ancestor_id": (
|
|
277
|
+
None
|
|
278
|
+
if candidate.common_ancestor_id is None
|
|
279
|
+
else candidate.common_ancestor_id.value
|
|
280
|
+
),
|
|
281
|
+
}
|
|
282
|
+
if candidate.objective_resolution_receipt is not None:
|
|
283
|
+
record["objective_resolution"] = (
|
|
284
|
+
candidate.objective_resolution_receipt.to_record()
|
|
285
|
+
)
|
|
286
|
+
return record
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _archive_decision_record(decision: ParetoDecision) -> dict[str, object]:
|
|
290
|
+
"""Project archive evidence without copying evaluator free text."""
|
|
291
|
+
|
|
292
|
+
if type(decision) is not ParetoDecision:
|
|
293
|
+
raise TypeError("decision must be an exact ParetoDecision")
|
|
294
|
+
ParetoDecision.__post_init__(decision)
|
|
295
|
+
record = decision.to_trace_record()
|
|
296
|
+
record["failure_details"] = [
|
|
297
|
+
{
|
|
298
|
+
"reason": reason.value,
|
|
299
|
+
"detail_sha256": hashlib.sha256(
|
|
300
|
+
detail.encode("utf-8", errors="strict")
|
|
301
|
+
).hexdigest(),
|
|
302
|
+
}
|
|
303
|
+
for reason, detail in decision.failure_details
|
|
304
|
+
]
|
|
305
|
+
record["failure_detail_projection"] = "content_minimized_sha256"
|
|
306
|
+
return record
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _cache_misses(snapshot: dict[str, int | None]) -> int:
|
|
310
|
+
value = snapshot.get("misses")
|
|
311
|
+
if type(value) is not int or value < 0:
|
|
312
|
+
raise RuntimeError("engine evaluation cache omitted exact miss accounting")
|
|
313
|
+
return value
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@dataclass(frozen=True, slots=True)
|
|
317
|
+
class CampaignParentLane:
|
|
318
|
+
"""One explicitly named parent role selected for a portfolio stage."""
|
|
319
|
+
|
|
320
|
+
lane_id: str
|
|
321
|
+
parent: EvolutionCandidate
|
|
322
|
+
|
|
323
|
+
def __post_init__(self) -> None:
|
|
324
|
+
_require_role_token(self.lane_id, "lane_id")
|
|
325
|
+
if type(self.parent) is not EvolutionCandidate:
|
|
326
|
+
raise TypeError("parent must be an exact EvolutionCandidate")
|
|
327
|
+
EvolutionCandidate.__post_init__(self.parent)
|
|
328
|
+
|
|
329
|
+
def to_record(self) -> dict[str, object]:
|
|
330
|
+
self.__post_init__()
|
|
331
|
+
return {
|
|
332
|
+
"lane_id": self.lane_id,
|
|
333
|
+
"parent_candidate_id": self.parent.candidate_id.value,
|
|
334
|
+
"parent_configuration_sha256": (self.parent.occurrence.configuration_hash),
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@dataclass(frozen=True, slots=True)
|
|
339
|
+
class CampaignDecisionSlot:
|
|
340
|
+
"""One named campaign decision bound to exactly one parent lane.
|
|
341
|
+
|
|
342
|
+
The current campaign protocol admits one primary slot per lane. Making the
|
|
343
|
+
identity explicit now prevents workload adapters from inferring scientific
|
|
344
|
+
roles from tuple position and leaves a typed extension point for matched
|
|
345
|
+
treatment/control slots later.
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
slot_id: str
|
|
349
|
+
lane_id: str
|
|
350
|
+
role_id: str = "primary"
|
|
351
|
+
|
|
352
|
+
def __post_init__(self) -> None:
|
|
353
|
+
_require_role_token(self.slot_id, "slot_id")
|
|
354
|
+
_require_role_token(self.lane_id, "lane_id")
|
|
355
|
+
_require_role_token(self.role_id, "role_id")
|
|
356
|
+
|
|
357
|
+
def to_record(self) -> dict[str, object]:
|
|
358
|
+
self.__post_init__()
|
|
359
|
+
return {
|
|
360
|
+
"slot_id": self.slot_id,
|
|
361
|
+
"lane_id": self.lane_id,
|
|
362
|
+
"role_id": self.role_id,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@dataclass(frozen=True, slots=True)
|
|
367
|
+
class CampaignParentSelection:
|
|
368
|
+
"""Parents selected from one exact optimizer state plus policy evidence."""
|
|
369
|
+
|
|
370
|
+
parents: tuple[EvolutionCandidate, ...]
|
|
371
|
+
evidence: FrozenJsonObject
|
|
372
|
+
lanes: tuple[CampaignParentLane, ...] = ()
|
|
373
|
+
decision_slots: tuple[CampaignDecisionSlot, ...] = ()
|
|
374
|
+
|
|
375
|
+
def __post_init__(self) -> None:
|
|
376
|
+
if type(self.parents) is not tuple or not self.parents:
|
|
377
|
+
raise ValueError("campaign parent selection requires parents")
|
|
378
|
+
if any(type(value) is not EvolutionCandidate for value in self.parents):
|
|
379
|
+
raise TypeError("parents must contain exact EvolutionCandidate values")
|
|
380
|
+
for value in self.parents:
|
|
381
|
+
EvolutionCandidate.__post_init__(value)
|
|
382
|
+
if len({value.candidate_id for value in self.parents}) != len(self.parents):
|
|
383
|
+
raise ValueError("campaign parents must be distinct occurrences")
|
|
384
|
+
if type(self.evidence) is not FrozenJsonObject:
|
|
385
|
+
raise TypeError("parent-selection evidence must be frozen typed JSON")
|
|
386
|
+
if freeze_json(self.evidence) is not self.evidence:
|
|
387
|
+
raise TypeError("parent-selection evidence must already be frozen")
|
|
388
|
+
lanes = self.lanes
|
|
389
|
+
if not lanes:
|
|
390
|
+
lanes = tuple(
|
|
391
|
+
CampaignParentLane(
|
|
392
|
+
lane_id=f"parent_{index + 1:04d}",
|
|
393
|
+
parent=parent,
|
|
394
|
+
)
|
|
395
|
+
for index, parent in enumerate(self.parents)
|
|
396
|
+
)
|
|
397
|
+
object.__setattr__(self, "lanes", lanes)
|
|
398
|
+
if type(lanes) is not tuple or any(
|
|
399
|
+
type(value) is not CampaignParentLane for value in lanes
|
|
400
|
+
):
|
|
401
|
+
raise TypeError("lanes must contain exact CampaignParentLane values")
|
|
402
|
+
for lane in lanes:
|
|
403
|
+
CampaignParentLane.__post_init__(lane)
|
|
404
|
+
if len(lanes) != len(self.parents) or any(
|
|
405
|
+
lane.parent is not parent
|
|
406
|
+
for lane, parent in zip(lanes, self.parents, strict=True)
|
|
407
|
+
):
|
|
408
|
+
raise ValueError("parent lanes must align exactly with selected parents")
|
|
409
|
+
lane_ids = tuple(lane.lane_id for lane in lanes)
|
|
410
|
+
if len(set(lane_ids)) != len(lane_ids):
|
|
411
|
+
raise ValueError("parent lane IDs must be unique")
|
|
412
|
+
|
|
413
|
+
slots = self.decision_slots
|
|
414
|
+
if not slots:
|
|
415
|
+
slots = tuple(
|
|
416
|
+
CampaignDecisionSlot(
|
|
417
|
+
slot_id=f"{lane.lane_id}.primary",
|
|
418
|
+
lane_id=lane.lane_id,
|
|
419
|
+
)
|
|
420
|
+
for lane in lanes
|
|
421
|
+
)
|
|
422
|
+
object.__setattr__(self, "decision_slots", slots)
|
|
423
|
+
if type(slots) is not tuple or any(
|
|
424
|
+
type(value) is not CampaignDecisionSlot for value in slots
|
|
425
|
+
):
|
|
426
|
+
raise TypeError(
|
|
427
|
+
"decision_slots must contain exact CampaignDecisionSlot values"
|
|
428
|
+
)
|
|
429
|
+
for slot in slots:
|
|
430
|
+
CampaignDecisionSlot.__post_init__(slot)
|
|
431
|
+
slot_ids = tuple(slot.slot_id for slot in slots)
|
|
432
|
+
if len(set(slot_ids)) != len(slot_ids):
|
|
433
|
+
raise ValueError("campaign decision slot IDs must be unique")
|
|
434
|
+
if tuple(slot.lane_id for slot in slots) != lane_ids:
|
|
435
|
+
raise ValueError(
|
|
436
|
+
"bounded campaign selection requires one ordered slot per lane"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
_PARENT_SELECTION_PROGRESS_DOMAIN = (
|
|
441
|
+
b"agent-evolve:campaign-parent-selection-progress:v1\x00"
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@dataclass(frozen=True, slots=True)
|
|
446
|
+
class CampaignParentSelectionProgress:
|
|
447
|
+
"""Authenticated workload-neutral archive transition from one stage."""
|
|
448
|
+
|
|
449
|
+
generation: int
|
|
450
|
+
stage_kind: CampaignGenerationKind
|
|
451
|
+
stage_request_sha256: str
|
|
452
|
+
stage_receipt_sha256: str
|
|
453
|
+
pre_archive_sha256: str
|
|
454
|
+
post_archive_sha256: str
|
|
455
|
+
utility_id: str
|
|
456
|
+
utility_version: int
|
|
457
|
+
utility_definition_sha256: str
|
|
458
|
+
pre_utility_snapshot_sha256: str
|
|
459
|
+
post_utility_snapshot_sha256: str
|
|
460
|
+
pre_scalar_utility_hex: str | None
|
|
461
|
+
post_scalar_utility_hex: str | None
|
|
462
|
+
|
|
463
|
+
def __post_init__(self) -> None:
|
|
464
|
+
if type(self.generation) is not int or self.generation <= 0:
|
|
465
|
+
raise ValueError("progress generation must be a positive exact integer")
|
|
466
|
+
if type(self.stage_kind) is not CampaignGenerationKind:
|
|
467
|
+
raise TypeError("stage_kind must be an exact CampaignGenerationKind")
|
|
468
|
+
for name in (
|
|
469
|
+
"stage_request_sha256",
|
|
470
|
+
"stage_receipt_sha256",
|
|
471
|
+
"pre_archive_sha256",
|
|
472
|
+
"post_archive_sha256",
|
|
473
|
+
"utility_definition_sha256",
|
|
474
|
+
"pre_utility_snapshot_sha256",
|
|
475
|
+
"post_utility_snapshot_sha256",
|
|
476
|
+
):
|
|
477
|
+
require_sha256(getattr(self, name), name)
|
|
478
|
+
_require_role_token(self.utility_id, "utility_id")
|
|
479
|
+
if type(self.utility_version) is not int or self.utility_version <= 0:
|
|
480
|
+
raise ValueError("utility_version must be a positive exact integer")
|
|
481
|
+
scalar_values = (
|
|
482
|
+
self.pre_scalar_utility_hex,
|
|
483
|
+
self.post_scalar_utility_hex,
|
|
484
|
+
)
|
|
485
|
+
if (scalar_values[0] is None) is not (scalar_values[1] is None):
|
|
486
|
+
raise ValueError("scalar utility progress must be complete or absent")
|
|
487
|
+
if scalar_values[0] is not None:
|
|
488
|
+
for name, value in zip(
|
|
489
|
+
("pre_scalar_utility_hex", "post_scalar_utility_hex"),
|
|
490
|
+
scalar_values,
|
|
491
|
+
strict=True,
|
|
492
|
+
):
|
|
493
|
+
if type(value) is not str:
|
|
494
|
+
raise TypeError(f"{name} must be an exact string or None")
|
|
495
|
+
try:
|
|
496
|
+
parsed = float.fromhex(value)
|
|
497
|
+
except ValueError as error:
|
|
498
|
+
raise ValueError(f"{name} must be a float hex string") from error
|
|
499
|
+
if not math.isfinite(parsed) or parsed < 0.0 or parsed.hex() != value:
|
|
500
|
+
raise ValueError(
|
|
501
|
+
f"{name} must encode a canonical non-negative utility"
|
|
502
|
+
)
|
|
503
|
+
if float.fromhex(scalar_values[1]) < float.fromhex(scalar_values[0]):
|
|
504
|
+
raise ValueError("authenticated archive utility cannot decrease")
|
|
505
|
+
|
|
506
|
+
@property
|
|
507
|
+
def archive_changed(self) -> bool:
|
|
508
|
+
return self.pre_archive_sha256 != self.post_archive_sha256
|
|
509
|
+
|
|
510
|
+
@property
|
|
511
|
+
def utility_signal_available(self) -> bool:
|
|
512
|
+
return self.pre_scalar_utility_hex is not None
|
|
513
|
+
|
|
514
|
+
@property
|
|
515
|
+
def utility_improved(self) -> bool | None:
|
|
516
|
+
if not self.utility_signal_available:
|
|
517
|
+
return None
|
|
518
|
+
assert self.pre_scalar_utility_hex is not None
|
|
519
|
+
assert self.post_scalar_utility_hex is not None
|
|
520
|
+
return float.fromhex(self.post_scalar_utility_hex) > float.fromhex(
|
|
521
|
+
self.pre_scalar_utility_hex
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
525
|
+
self.__post_init__()
|
|
526
|
+
return {
|
|
527
|
+
"generation": self.generation,
|
|
528
|
+
"stage_kind": self.stage_kind.value,
|
|
529
|
+
"stage_request_sha256": self.stage_request_sha256,
|
|
530
|
+
"stage_receipt_sha256": self.stage_receipt_sha256,
|
|
531
|
+
"pre_archive_sha256": self.pre_archive_sha256,
|
|
532
|
+
"post_archive_sha256": self.post_archive_sha256,
|
|
533
|
+
"archive_changed": self.archive_changed,
|
|
534
|
+
"utility_id": self.utility_id,
|
|
535
|
+
"utility_version": self.utility_version,
|
|
536
|
+
"utility_definition_sha256": self.utility_definition_sha256,
|
|
537
|
+
"pre_utility_snapshot_sha256": self.pre_utility_snapshot_sha256,
|
|
538
|
+
"post_utility_snapshot_sha256": self.post_utility_snapshot_sha256,
|
|
539
|
+
"pre_scalar_utility_hex": self.pre_scalar_utility_hex,
|
|
540
|
+
"post_scalar_utility_hex": self.post_scalar_utility_hex,
|
|
541
|
+
"utility_signal_available": self.utility_signal_available,
|
|
542
|
+
"utility_improved": self.utility_improved,
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def progress_sha256(self) -> str:
|
|
547
|
+
return hashlib.sha256(
|
|
548
|
+
_PARENT_SELECTION_PROGRESS_DOMAIN
|
|
549
|
+
+ _canonical_text(self._unsigned_record()).encode("ascii")
|
|
550
|
+
).hexdigest()
|
|
551
|
+
|
|
552
|
+
def to_record(self) -> dict[str, object]:
|
|
553
|
+
return {
|
|
554
|
+
**self._unsigned_record(),
|
|
555
|
+
"progress_sha256": self.progress_sha256,
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
@dataclass(frozen=True, slots=True)
|
|
560
|
+
class _PendingCampaignParentSelectionProgress:
|
|
561
|
+
generation: int
|
|
562
|
+
stage_kind: CampaignGenerationKind
|
|
563
|
+
stage_request_sha256: str
|
|
564
|
+
stage_receipt_sha256: str
|
|
565
|
+
pre_archive_sha256: str
|
|
566
|
+
post_archive_sha256: str
|
|
567
|
+
pre_utility: ArchiveUtilitySnapshot
|
|
568
|
+
|
|
569
|
+
def __post_init__(self) -> None:
|
|
570
|
+
if type(self.generation) is not int or self.generation <= 0:
|
|
571
|
+
raise ValueError("pending progress generation must be positive")
|
|
572
|
+
if type(self.stage_kind) is not CampaignGenerationKind:
|
|
573
|
+
raise TypeError("pending stage kind must be exact")
|
|
574
|
+
for name in (
|
|
575
|
+
"stage_request_sha256",
|
|
576
|
+
"stage_receipt_sha256",
|
|
577
|
+
"pre_archive_sha256",
|
|
578
|
+
"post_archive_sha256",
|
|
579
|
+
):
|
|
580
|
+
require_sha256(getattr(self, name), name)
|
|
581
|
+
if type(self.pre_utility) is not ArchiveUtilitySnapshot:
|
|
582
|
+
raise TypeError("pre_utility must be an exact ArchiveUtilitySnapshot")
|
|
583
|
+
ArchiveUtilitySnapshot.__post_init__(self.pre_utility)
|
|
584
|
+
|
|
585
|
+
def complete(
|
|
586
|
+
self,
|
|
587
|
+
post_utility: ArchiveUtilitySnapshot,
|
|
588
|
+
) -> CampaignParentSelectionProgress:
|
|
589
|
+
self.__post_init__()
|
|
590
|
+
if type(post_utility) is not ArchiveUtilitySnapshot:
|
|
591
|
+
raise TypeError("post_utility must be an exact ArchiveUtilitySnapshot")
|
|
592
|
+
ArchiveUtilitySnapshot.__post_init__(post_utility)
|
|
593
|
+
if (
|
|
594
|
+
post_utility.generation != self.generation + 1
|
|
595
|
+
or post_utility.utility_id != self.pre_utility.utility_id
|
|
596
|
+
or post_utility.utility_version != self.pre_utility.utility_version
|
|
597
|
+
or post_utility.definition_sha256 != self.pre_utility.definition_sha256
|
|
598
|
+
or post_utility.archive_sha256 != self.post_archive_sha256
|
|
599
|
+
):
|
|
600
|
+
raise ValueError(
|
|
601
|
+
"post-stage utility snapshot does not close pending progress"
|
|
602
|
+
)
|
|
603
|
+
return CampaignParentSelectionProgress(
|
|
604
|
+
generation=self.generation,
|
|
605
|
+
stage_kind=self.stage_kind,
|
|
606
|
+
stage_request_sha256=self.stage_request_sha256,
|
|
607
|
+
stage_receipt_sha256=self.stage_receipt_sha256,
|
|
608
|
+
pre_archive_sha256=self.pre_archive_sha256,
|
|
609
|
+
post_archive_sha256=self.post_archive_sha256,
|
|
610
|
+
utility_id=self.pre_utility.utility_id,
|
|
611
|
+
utility_version=self.pre_utility.utility_version,
|
|
612
|
+
utility_definition_sha256=self.pre_utility.definition_sha256,
|
|
613
|
+
pre_utility_snapshot_sha256=self.pre_utility.snapshot_sha256,
|
|
614
|
+
post_utility_snapshot_sha256=post_utility.snapshot_sha256,
|
|
615
|
+
pre_scalar_utility_hex=self.pre_utility.scalar_utility_hex,
|
|
616
|
+
post_scalar_utility_hex=post_utility.scalar_utility_hex,
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def _validate_parent_selection_progress(
|
|
621
|
+
progress: tuple[CampaignParentSelectionProgress, ...],
|
|
622
|
+
*,
|
|
623
|
+
optimizer_generation: int,
|
|
624
|
+
) -> None:
|
|
625
|
+
if type(progress) is not tuple or any(
|
|
626
|
+
type(value) is not CampaignParentSelectionProgress for value in progress
|
|
627
|
+
):
|
|
628
|
+
raise TypeError(
|
|
629
|
+
"progress must contain exact CampaignParentSelectionProgress values"
|
|
630
|
+
)
|
|
631
|
+
for value in progress:
|
|
632
|
+
CampaignParentSelectionProgress.__post_init__(value)
|
|
633
|
+
if tuple(value.generation for value in progress) != tuple(
|
|
634
|
+
range(1, len(progress) + 1)
|
|
635
|
+
):
|
|
636
|
+
raise ValueError("parent-selection progress must be contiguous and ordered")
|
|
637
|
+
if len(progress) != optimizer_generation:
|
|
638
|
+
raise ValueError(
|
|
639
|
+
"parent-selection progress must cover the exact optimizer generation"
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
@runtime_checkable
|
|
644
|
+
class CampaignParentSelectionPort(Protocol):
|
|
645
|
+
"""Experiment-owned, workload-neutral parent-selection seam."""
|
|
646
|
+
|
|
647
|
+
def select(
|
|
648
|
+
self,
|
|
649
|
+
state: OptimizerState,
|
|
650
|
+
*,
|
|
651
|
+
task_sha256: str,
|
|
652
|
+
parent_count: int,
|
|
653
|
+
rotation_index: int,
|
|
654
|
+
progress: tuple[CampaignParentSelectionProgress, ...] = (),
|
|
655
|
+
archive_utility: ArchiveUtilitySnapshot | None = None,
|
|
656
|
+
) -> CampaignParentSelection: ...
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
@dataclass(frozen=True, slots=True)
|
|
660
|
+
class ArchiveReservoirCampaignParentSelector:
|
|
661
|
+
"""Use the existing ranked-reservoir policy behind the campaign seam."""
|
|
662
|
+
|
|
663
|
+
reservoir_limit: int = 8
|
|
664
|
+
policy: TaskKeyedArchiveReservoirParentPolicy = field(
|
|
665
|
+
default_factory=TaskKeyedArchiveReservoirParentPolicy
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
def __post_init__(self) -> None:
|
|
669
|
+
if type(self.reservoir_limit) is not int or self.reservoir_limit <= 0:
|
|
670
|
+
raise ValueError("reservoir_limit must be positive")
|
|
671
|
+
if type(self.policy) is not TaskKeyedArchiveReservoirParentPolicy:
|
|
672
|
+
raise TypeError("policy must be an exact reservoir parent policy")
|
|
673
|
+
|
|
674
|
+
def select(
|
|
675
|
+
self,
|
|
676
|
+
state: OptimizerState,
|
|
677
|
+
*,
|
|
678
|
+
task_sha256: str,
|
|
679
|
+
parent_count: int,
|
|
680
|
+
rotation_index: int,
|
|
681
|
+
progress: tuple[CampaignParentSelectionProgress, ...] = (),
|
|
682
|
+
archive_utility: ArchiveUtilitySnapshot | None = None,
|
|
683
|
+
) -> CampaignParentSelection:
|
|
684
|
+
del archive_utility
|
|
685
|
+
_validate_parent_selection_progress(
|
|
686
|
+
progress,
|
|
687
|
+
optimizer_generation=state.generation,
|
|
688
|
+
)
|
|
689
|
+
selected = self.policy.select(
|
|
690
|
+
state,
|
|
691
|
+
task_sha256=task_sha256,
|
|
692
|
+
expected_archive_snapshot_hash=state.archive_snapshot_hash,
|
|
693
|
+
reservoir_limit=self.reservoir_limit,
|
|
694
|
+
parent_count=parent_count,
|
|
695
|
+
rotation_index=rotation_index,
|
|
696
|
+
)
|
|
697
|
+
lanes = tuple(
|
|
698
|
+
CampaignParentLane(
|
|
699
|
+
lane_id=f"reservoir_{index + 1:04d}",
|
|
700
|
+
parent=parent,
|
|
701
|
+
)
|
|
702
|
+
for index, parent in enumerate(selected.parents)
|
|
703
|
+
)
|
|
704
|
+
return CampaignParentSelection(
|
|
705
|
+
parents=selected.parents,
|
|
706
|
+
evidence=_object(selected.receipt.to_trace_record()),
|
|
707
|
+
lanes=lanes,
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
@dataclass(frozen=True, slots=True)
|
|
712
|
+
class ArchiveEliteCampaignParentSelector:
|
|
713
|
+
"""Use the outcome-relation-neutral archive-front policy in a campaign."""
|
|
714
|
+
|
|
715
|
+
policy: TaskKeyedArchiveEliteParentPolicy = field(
|
|
716
|
+
default_factory=TaskKeyedArchiveEliteParentPolicy
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
def __post_init__(self) -> None:
|
|
720
|
+
if type(self.policy) is not TaskKeyedArchiveEliteParentPolicy:
|
|
721
|
+
raise TypeError("policy must be an exact elite parent policy")
|
|
722
|
+
|
|
723
|
+
def select(
|
|
724
|
+
self,
|
|
725
|
+
state: OptimizerState,
|
|
726
|
+
*,
|
|
727
|
+
task_sha256: str,
|
|
728
|
+
parent_count: int,
|
|
729
|
+
rotation_index: int,
|
|
730
|
+
progress: tuple[CampaignParentSelectionProgress, ...] = (),
|
|
731
|
+
archive_utility: ArchiveUtilitySnapshot | None = None,
|
|
732
|
+
) -> CampaignParentSelection:
|
|
733
|
+
del archive_utility
|
|
734
|
+
_validate_parent_selection_progress(
|
|
735
|
+
progress,
|
|
736
|
+
optimizer_generation=state.generation,
|
|
737
|
+
)
|
|
738
|
+
selected = self.policy.select(
|
|
739
|
+
state,
|
|
740
|
+
task_sha256=task_sha256,
|
|
741
|
+
expected_archive_snapshot_hash=state.archive_snapshot_hash,
|
|
742
|
+
parent_count=parent_count,
|
|
743
|
+
rotation_index=rotation_index,
|
|
744
|
+
)
|
|
745
|
+
lanes = tuple(
|
|
746
|
+
CampaignParentLane(
|
|
747
|
+
lane_id=f"elite_{index + 1:04d}",
|
|
748
|
+
parent=parent,
|
|
749
|
+
)
|
|
750
|
+
for index, parent in enumerate(selected.parents)
|
|
751
|
+
)
|
|
752
|
+
return CampaignParentSelection(
|
|
753
|
+
parents=selected.parents,
|
|
754
|
+
evidence=_object(selected.receipt.to_trace_record()),
|
|
755
|
+
lanes=lanes,
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
_DIVERSE_ELITE_PARENT_POLICY_ID = "archive_diverse_elite_campaign_parent"
|
|
760
|
+
_DIVERSE_ELITE_PARENT_POLICY_VERSION = 1
|
|
761
|
+
_DIVERSE_ELITE_PARENT_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
762
|
+
b"agent-evolve:archive-diverse-elite-campaign-parent:v1:"
|
|
763
|
+
b"elite=task-keyed-current-front-member;"
|
|
764
|
+
b"explorer=max-canonical-typed-patch-operation-distance-on-current-front;"
|
|
765
|
+
b"ties=task-and-snapshot-keyed-sha256;"
|
|
766
|
+
b"singleton=structurally-most-distant-eligible-history-fallback;"
|
|
767
|
+
b"workload-semantics=false;dominated-parent=false"
|
|
768
|
+
).hexdigest()
|
|
769
|
+
_DIVERSE_ELITE_PARENT_ROTATION_DOMAIN = (
|
|
770
|
+
b"agent-evolve:archive-diverse-elite-campaign-parent:rotation:v1\x00"
|
|
771
|
+
)
|
|
772
|
+
_DIVERSE_ELITE_PARENT_TIE_DOMAIN = (
|
|
773
|
+
b"agent-evolve:archive-diverse-elite-campaign-parent:tie:v1\x00"
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
@dataclass(frozen=True, slots=True)
|
|
778
|
+
class ArchiveDiverseEliteCampaignParentSelector:
|
|
779
|
+
"""Select two nondominated parents with workload-neutral structural spread.
|
|
780
|
+
|
|
781
|
+
The first lane rotates over the authenticated current Pareto front. The
|
|
782
|
+
second lane is the distinct current-front configuration requiring the
|
|
783
|
+
largest canonical typed-patch operation set from that anchor. This keeps
|
|
784
|
+
exploration on known trade-off-efficient material while exposing generic
|
|
785
|
+
structural diversity to mutation and crossover. A singleton front falls
|
|
786
|
+
back explicitly to the structurally most distant eligible history member,
|
|
787
|
+
because campaign parent occurrences must remain distinct.
|
|
788
|
+
"""
|
|
789
|
+
|
|
790
|
+
def select(
|
|
791
|
+
self,
|
|
792
|
+
state: OptimizerState,
|
|
793
|
+
*,
|
|
794
|
+
task_sha256: str,
|
|
795
|
+
parent_count: int,
|
|
796
|
+
rotation_index: int,
|
|
797
|
+
progress: tuple[CampaignParentSelectionProgress, ...] = (),
|
|
798
|
+
archive_utility: ArchiveUtilitySnapshot | None = None,
|
|
799
|
+
) -> CampaignParentSelection:
|
|
800
|
+
del archive_utility
|
|
801
|
+
if type(self) is not ArchiveDiverseEliteCampaignParentSelector:
|
|
802
|
+
raise TypeError("selector must be an exact diverse-elite selector")
|
|
803
|
+
OptimizerState.__post_init__(state)
|
|
804
|
+
_validate_parent_selection_progress(
|
|
805
|
+
progress,
|
|
806
|
+
optimizer_generation=state.generation,
|
|
807
|
+
)
|
|
808
|
+
require_sha256(task_sha256, "task_sha256")
|
|
809
|
+
if type(parent_count) is not int or parent_count != 2:
|
|
810
|
+
raise ValueError(
|
|
811
|
+
"diverse-elite campaign parent selection requires exactly two lanes"
|
|
812
|
+
)
|
|
813
|
+
if type(rotation_index) is not int or not 0 <= rotation_index < (1 << 63):
|
|
814
|
+
raise ValueError("rotation_index must be an exact non-negative int63")
|
|
815
|
+
|
|
816
|
+
candidates = state.archive.front_candidates
|
|
817
|
+
references = state.archive.front_references
|
|
818
|
+
if not candidates or len(candidates) != len(references):
|
|
819
|
+
raise ValueError("current archive front is empty or internally misaligned")
|
|
820
|
+
by_id = {candidate.candidate_id: candidate for candidate in candidates}
|
|
821
|
+
if (
|
|
822
|
+
tuple(by_id[reference.candidate_id] for reference in references)
|
|
823
|
+
!= candidates
|
|
824
|
+
):
|
|
825
|
+
raise ValueError("current archive candidates differ from front references")
|
|
826
|
+
|
|
827
|
+
rotation_digest = hashlib.sha256(
|
|
828
|
+
_DIVERSE_ELITE_PARENT_ROTATION_DOMAIN
|
|
829
|
+
+ bytes.fromhex(task_sha256)
|
|
830
|
+
+ bytes.fromhex(state.archive_snapshot_hash)
|
|
831
|
+
).digest()
|
|
832
|
+
rotation_anchor = int.from_bytes(rotation_digest, "big") % len(candidates)
|
|
833
|
+
elite_ordinal = (rotation_anchor + rotation_index) % len(candidates)
|
|
834
|
+
elite = candidates[elite_ordinal]
|
|
835
|
+
|
|
836
|
+
distance_records: list[dict[str, object]] = []
|
|
837
|
+
if len(candidates) == 1:
|
|
838
|
+
explorer_pool = tuple(
|
|
839
|
+
candidate
|
|
840
|
+
for candidate in state.candidates
|
|
841
|
+
if candidate.candidate_id != elite.candidate_id
|
|
842
|
+
and candidate.occurrence.configuration_hash
|
|
843
|
+
!= elite.occurrence.configuration_hash
|
|
844
|
+
and candidate.valid
|
|
845
|
+
and candidate.operator_compliant
|
|
846
|
+
and candidate.evidence_compliant
|
|
847
|
+
and candidate.objectives
|
|
848
|
+
)
|
|
849
|
+
if not explorer_pool:
|
|
850
|
+
raise ValueError(
|
|
851
|
+
"diverse-elite selection requires two eligible distinct "
|
|
852
|
+
"candidate occurrences"
|
|
853
|
+
)
|
|
854
|
+
explorer_sources = tuple(
|
|
855
|
+
(len(candidates) + index, candidate)
|
|
856
|
+
for index, candidate in enumerate(explorer_pool)
|
|
857
|
+
)
|
|
858
|
+
fallback_reason = "singleton_front_structural_history_fallback"
|
|
859
|
+
else:
|
|
860
|
+
explorer_sources = tuple(
|
|
861
|
+
(ordinal, candidate)
|
|
862
|
+
for ordinal, candidate in enumerate(candidates)
|
|
863
|
+
if ordinal != elite_ordinal
|
|
864
|
+
)
|
|
865
|
+
fallback_reason = "none"
|
|
866
|
+
|
|
867
|
+
scored: list[tuple[int, bytes, int, EvolutionCandidate, str]] = []
|
|
868
|
+
for ordinal, candidate in explorer_sources:
|
|
869
|
+
patch = derive_patch(
|
|
870
|
+
elite.configuration,
|
|
871
|
+
candidate.configuration,
|
|
872
|
+
base_candidate_id=elite.candidate_id,
|
|
873
|
+
target_candidate_id=candidate.candidate_id,
|
|
874
|
+
)
|
|
875
|
+
operation_distance = len(patch.operations)
|
|
876
|
+
tie_digest = hashlib.sha256(
|
|
877
|
+
_DIVERSE_ELITE_PARENT_TIE_DOMAIN
|
|
878
|
+
+ bytes.fromhex(task_sha256)
|
|
879
|
+
+ bytes.fromhex(state.archive_snapshot_hash)
|
|
880
|
+
+ rotation_index.to_bytes(8, "big", signed=False)
|
|
881
|
+
+ bytes.fromhex(candidate.occurrence.configuration_hash)
|
|
882
|
+
).digest()
|
|
883
|
+
scored.append(
|
|
884
|
+
(
|
|
885
|
+
operation_distance,
|
|
886
|
+
tie_digest,
|
|
887
|
+
ordinal,
|
|
888
|
+
candidate,
|
|
889
|
+
patch.patch_hash,
|
|
890
|
+
)
|
|
891
|
+
)
|
|
892
|
+
distance_records.append(
|
|
893
|
+
{
|
|
894
|
+
"candidate_id": candidate.candidate_id.value,
|
|
895
|
+
"configuration_hash": candidate.occurrence.configuration_hash,
|
|
896
|
+
"typed_patch_operation_distance": operation_distance,
|
|
897
|
+
"typed_patch_sha256": patch.patch_hash,
|
|
898
|
+
}
|
|
899
|
+
)
|
|
900
|
+
maximum_distance = max(value[0] for value in scored)
|
|
901
|
+
finalists = tuple(value for value in scored if value[0] == maximum_distance)
|
|
902
|
+
selected = min(finalists, key=lambda value: (value[1], value[2]))
|
|
903
|
+
_, _, explorer_ordinal, explorer, _ = selected
|
|
904
|
+
|
|
905
|
+
selected_parents = (elite, explorer)
|
|
906
|
+
lanes = (
|
|
907
|
+
CampaignParentLane(lane_id="elite", parent=elite),
|
|
908
|
+
CampaignParentLane(lane_id="explorer", parent=explorer),
|
|
909
|
+
)
|
|
910
|
+
return CampaignParentSelection(
|
|
911
|
+
parents=selected_parents,
|
|
912
|
+
lanes=lanes,
|
|
913
|
+
evidence=_object(
|
|
914
|
+
{
|
|
915
|
+
"schema_version": 1,
|
|
916
|
+
"policy_id": _DIVERSE_ELITE_PARENT_POLICY_ID,
|
|
917
|
+
"policy_version": _DIVERSE_ELITE_PARENT_POLICY_VERSION,
|
|
918
|
+
"policy_definition_sha256": (
|
|
919
|
+
_DIVERSE_ELITE_PARENT_POLICY_DEFINITION_SHA256
|
|
920
|
+
),
|
|
921
|
+
"task_sha256": task_sha256,
|
|
922
|
+
"optimizer_generation": state.generation,
|
|
923
|
+
"archive_snapshot_hash": state.archive_snapshot_hash,
|
|
924
|
+
"eligible_front": [
|
|
925
|
+
reference.to_trace_record() for reference in references
|
|
926
|
+
],
|
|
927
|
+
"rotation_index": rotation_index,
|
|
928
|
+
"rotation_anchor": rotation_anchor,
|
|
929
|
+
"elite_ordinal": elite_ordinal,
|
|
930
|
+
"explorer_ordinal": explorer_ordinal,
|
|
931
|
+
"maximum_typed_patch_operation_distance": maximum_distance,
|
|
932
|
+
"distance_evidence": sorted(
|
|
933
|
+
distance_records,
|
|
934
|
+
key=lambda value: str(value["candidate_id"]),
|
|
935
|
+
),
|
|
936
|
+
"fallback_reason": fallback_reason,
|
|
937
|
+
"selected_parent_ids": [
|
|
938
|
+
parent.candidate_id.value for parent in selected_parents
|
|
939
|
+
],
|
|
940
|
+
"selected_parents_are_current_front_members": all(
|
|
941
|
+
parent.candidate_id in by_id for parent in selected_parents
|
|
942
|
+
),
|
|
943
|
+
"provider_fields_consulted": False,
|
|
944
|
+
"workload_semantics_consulted": False,
|
|
945
|
+
}
|
|
946
|
+
),
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
_STAGNATION_AWARE_PARENT_POLICY_ID = "stagnation_aware_diverse_campaign_parent"
|
|
951
|
+
_STAGNATION_AWARE_PARENT_POLICY_VERSION = 1
|
|
952
|
+
_STAGNATION_AWARE_PARENT_WINDOW = 2
|
|
953
|
+
_STAGNATION_AWARE_PARENT_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
954
|
+
b"agent-evolve:stagnation-aware-diverse-campaign-parent:v1:"
|
|
955
|
+
b"normal=archive-diverse-elite-campaign-parent-v1;"
|
|
956
|
+
b"trigger=two-consecutive-runtime-authenticated-stage-progress-receipts-with-"
|
|
957
|
+
b"no-increase-in-generic-scalar-archive-utility;"
|
|
958
|
+
b"elite=normal-policy-elite;"
|
|
959
|
+
b"stagnant-explorer=eligible-nonfront-history-member-maximizing-minimum-"
|
|
960
|
+
b"canonical-typed-patch-operation-distance-to-current-front;"
|
|
961
|
+
b"ties=task-snapshot-rotation-and-configuration-keyed-sha256;"
|
|
962
|
+
b"empty-pool=normal-policy-fallback;"
|
|
963
|
+
b"provider-fields=false;workload-semantics=false"
|
|
964
|
+
).hexdigest()
|
|
965
|
+
_STAGNATION_AWARE_PARENT_TIE_DOMAIN = (
|
|
966
|
+
b"agent-evolve:stagnation-aware-diverse-campaign-parent:tie:v1\x00"
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
@dataclass(frozen=True, slots=True)
|
|
971
|
+
class StagnationAwareDiverseCampaignParentSelector:
|
|
972
|
+
"""Switch one parent lane to structurally remote history after stagnation.
|
|
973
|
+
|
|
974
|
+
Normal generations use :class:`ArchiveDiverseEliteCampaignParentSelector`
|
|
975
|
+
exactly. Once the two most recent authenticated generation receipts both
|
|
976
|
+
report an unchanged archive snapshot, the elite lane remains untouched and
|
|
977
|
+
the explorer lane is sourced from eligible evaluated history outside the
|
|
978
|
+
current front. Its score is the minimum canonical typed-patch operation
|
|
979
|
+
distance to *every* current-front member, so the switch seeks a genuinely
|
|
980
|
+
different basin rather than a point remote from only one arbitrary anchor.
|
|
981
|
+
|
|
982
|
+
The detector and distance law consume no objective names, workload
|
|
983
|
+
semantics, prompts, provider fields, or model identity. If no eligible
|
|
984
|
+
nonfront source exists, selection falls back explicitly to the normal
|
|
985
|
+
diverse-elite policy and records that fact.
|
|
986
|
+
"""
|
|
987
|
+
|
|
988
|
+
policy_id = _STAGNATION_AWARE_PARENT_POLICY_ID
|
|
989
|
+
policy_version = _STAGNATION_AWARE_PARENT_POLICY_VERSION
|
|
990
|
+
definition_sha256 = _STAGNATION_AWARE_PARENT_POLICY_DEFINITION_SHA256
|
|
991
|
+
stagnation_window = _STAGNATION_AWARE_PARENT_WINDOW
|
|
992
|
+
|
|
993
|
+
def select(
|
|
994
|
+
self,
|
|
995
|
+
state: OptimizerState,
|
|
996
|
+
*,
|
|
997
|
+
task_sha256: str,
|
|
998
|
+
parent_count: int,
|
|
999
|
+
rotation_index: int,
|
|
1000
|
+
progress: tuple[CampaignParentSelectionProgress, ...] = (),
|
|
1001
|
+
archive_utility: ArchiveUtilitySnapshot | None = None,
|
|
1002
|
+
) -> CampaignParentSelection:
|
|
1003
|
+
if type(self) is not StagnationAwareDiverseCampaignParentSelector:
|
|
1004
|
+
raise TypeError("selector must be an exact stagnation-aware selector")
|
|
1005
|
+
OptimizerState.__post_init__(state)
|
|
1006
|
+
_validate_parent_selection_progress(
|
|
1007
|
+
progress,
|
|
1008
|
+
optimizer_generation=state.generation,
|
|
1009
|
+
)
|
|
1010
|
+
require_sha256(task_sha256, "task_sha256")
|
|
1011
|
+
if type(parent_count) is not int or parent_count != 2:
|
|
1012
|
+
raise ValueError(
|
|
1013
|
+
"stagnation-aware campaign parent selection requires exactly two lanes"
|
|
1014
|
+
)
|
|
1015
|
+
if type(rotation_index) is not int or not 0 <= rotation_index < (1 << 63):
|
|
1016
|
+
raise ValueError("rotation_index must be an exact non-negative int63")
|
|
1017
|
+
|
|
1018
|
+
normal = ArchiveDiverseEliteCampaignParentSelector().select(
|
|
1019
|
+
state,
|
|
1020
|
+
task_sha256=task_sha256,
|
|
1021
|
+
parent_count=parent_count,
|
|
1022
|
+
rotation_index=rotation_index,
|
|
1023
|
+
progress=progress,
|
|
1024
|
+
archive_utility=archive_utility,
|
|
1025
|
+
)
|
|
1026
|
+
receipt_window = progress[-self.stagnation_window :]
|
|
1027
|
+
if any(not receipt.utility_signal_available for receipt in progress):
|
|
1028
|
+
raise ValueError(
|
|
1029
|
+
"stagnation-aware selection requires authenticated scalar "
|
|
1030
|
+
"archive utility on every completed stage"
|
|
1031
|
+
)
|
|
1032
|
+
receipt_evidence = [receipt.to_record() for receipt in receipt_window]
|
|
1033
|
+
stagnation_triggered = len(receipt_window) == self.stagnation_window and all(
|
|
1034
|
+
receipt.utility_improved is False for receipt in receipt_window
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
selected_parents = normal.parents
|
|
1038
|
+
source_mode = "normal_diverse_elite"
|
|
1039
|
+
source_switch_applied = False
|
|
1040
|
+
selected_history_ordinal: int | None = None
|
|
1041
|
+
maximum_minimum_distance: int | None = None
|
|
1042
|
+
distance_records: list[dict[str, object]] = []
|
|
1043
|
+
|
|
1044
|
+
front = state.archive.front_candidates
|
|
1045
|
+
front_ids = {candidate.candidate_id for candidate in front}
|
|
1046
|
+
front_configuration_hashes = {
|
|
1047
|
+
candidate.occurrence.configuration_hash for candidate in front
|
|
1048
|
+
}
|
|
1049
|
+
eligible_history = tuple(
|
|
1050
|
+
(ordinal, candidate)
|
|
1051
|
+
for ordinal, candidate in enumerate(state.candidates)
|
|
1052
|
+
if candidate.candidate_id not in front_ids
|
|
1053
|
+
and candidate.occurrence.configuration_hash
|
|
1054
|
+
not in front_configuration_hashes
|
|
1055
|
+
and candidate.valid
|
|
1056
|
+
and candidate.operator_compliant
|
|
1057
|
+
and candidate.evidence_compliant
|
|
1058
|
+
and candidate.objectives
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
if stagnation_triggered and eligible_history:
|
|
1062
|
+
scored: list[tuple[int, bytes, int, EvolutionCandidate]] = []
|
|
1063
|
+
for history_ordinal, candidate in eligible_history:
|
|
1064
|
+
front_distances: list[dict[str, object]] = []
|
|
1065
|
+
operation_distances: list[int] = []
|
|
1066
|
+
for front_candidate in front:
|
|
1067
|
+
patch = derive_patch(
|
|
1068
|
+
front_candidate.configuration,
|
|
1069
|
+
candidate.configuration,
|
|
1070
|
+
base_candidate_id=front_candidate.candidate_id,
|
|
1071
|
+
target_candidate_id=candidate.candidate_id,
|
|
1072
|
+
)
|
|
1073
|
+
operation_distance = len(patch.operations)
|
|
1074
|
+
operation_distances.append(operation_distance)
|
|
1075
|
+
front_distances.append(
|
|
1076
|
+
{
|
|
1077
|
+
"front_candidate_id": (front_candidate.candidate_id.value),
|
|
1078
|
+
"front_configuration_sha256": (
|
|
1079
|
+
front_candidate.occurrence.configuration_hash
|
|
1080
|
+
),
|
|
1081
|
+
"typed_patch_operation_distance": operation_distance,
|
|
1082
|
+
"typed_patch_sha256": patch.patch_hash,
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
minimum_distance = min(operation_distances)
|
|
1086
|
+
tie_digest = hashlib.sha256(
|
|
1087
|
+
_STAGNATION_AWARE_PARENT_TIE_DOMAIN
|
|
1088
|
+
+ bytes.fromhex(task_sha256)
|
|
1089
|
+
+ bytes.fromhex(state.archive_snapshot_hash)
|
|
1090
|
+
+ rotation_index.to_bytes(8, "big", signed=False)
|
|
1091
|
+
+ bytes.fromhex(candidate.occurrence.configuration_hash)
|
|
1092
|
+
).digest()
|
|
1093
|
+
scored.append(
|
|
1094
|
+
(minimum_distance, tie_digest, history_ordinal, candidate)
|
|
1095
|
+
)
|
|
1096
|
+
distance_records.append(
|
|
1097
|
+
{
|
|
1098
|
+
"candidate_id": candidate.candidate_id.value,
|
|
1099
|
+
"configuration_sha256": (
|
|
1100
|
+
candidate.occurrence.configuration_hash
|
|
1101
|
+
),
|
|
1102
|
+
"history_ordinal": history_ordinal,
|
|
1103
|
+
"minimum_typed_patch_operation_distance_to_front": (
|
|
1104
|
+
minimum_distance
|
|
1105
|
+
),
|
|
1106
|
+
"front_distance_evidence": sorted(
|
|
1107
|
+
front_distances,
|
|
1108
|
+
key=lambda value: str(value["front_candidate_id"]),
|
|
1109
|
+
),
|
|
1110
|
+
}
|
|
1111
|
+
)
|
|
1112
|
+
maximum_minimum_distance = max(value[0] for value in scored)
|
|
1113
|
+
finalists = tuple(
|
|
1114
|
+
value for value in scored if value[0] == maximum_minimum_distance
|
|
1115
|
+
)
|
|
1116
|
+
_, _, selected_history_ordinal, explorer = min(
|
|
1117
|
+
finalists,
|
|
1118
|
+
key=lambda value: (value[1], value[2]),
|
|
1119
|
+
)
|
|
1120
|
+
selected_parents = (normal.parents[0], explorer)
|
|
1121
|
+
source_mode = "stagnation_remote_history"
|
|
1122
|
+
source_switch_applied = True
|
|
1123
|
+
elif stagnation_triggered:
|
|
1124
|
+
source_mode = "stagnation_triggered_normal_fallback"
|
|
1125
|
+
|
|
1126
|
+
lanes = (
|
|
1127
|
+
CampaignParentLane(lane_id="elite", parent=selected_parents[0]),
|
|
1128
|
+
CampaignParentLane(lane_id="explorer", parent=selected_parents[1]),
|
|
1129
|
+
)
|
|
1130
|
+
return CampaignParentSelection(
|
|
1131
|
+
parents=selected_parents,
|
|
1132
|
+
lanes=lanes,
|
|
1133
|
+
evidence=_object(
|
|
1134
|
+
{
|
|
1135
|
+
"schema_version": 1,
|
|
1136
|
+
"policy_id": self.policy_id,
|
|
1137
|
+
"policy_version": self.policy_version,
|
|
1138
|
+
"policy_definition_sha256": self.definition_sha256,
|
|
1139
|
+
"task_sha256": task_sha256,
|
|
1140
|
+
"optimizer_generation": state.generation,
|
|
1141
|
+
"archive_snapshot_sha256": state.archive_snapshot_hash,
|
|
1142
|
+
"rotation_index": rotation_index,
|
|
1143
|
+
"stagnation_window": self.stagnation_window,
|
|
1144
|
+
"observed_receipt_count": len(receipt_window),
|
|
1145
|
+
"stagnation_receipt_evidence": receipt_evidence,
|
|
1146
|
+
"stagnation_triggered": stagnation_triggered,
|
|
1147
|
+
"source_switch_applied": source_switch_applied,
|
|
1148
|
+
"source_mode": source_mode,
|
|
1149
|
+
"eligible_nonfront_history_count": len(eligible_history),
|
|
1150
|
+
"maximum_minimum_typed_patch_operation_distance_to_front": (
|
|
1151
|
+
maximum_minimum_distance
|
|
1152
|
+
),
|
|
1153
|
+
"selected_history_ordinal": selected_history_ordinal,
|
|
1154
|
+
"distance_evidence": sorted(
|
|
1155
|
+
distance_records,
|
|
1156
|
+
key=lambda value: str(value["candidate_id"]),
|
|
1157
|
+
),
|
|
1158
|
+
"normal_policy_evidence": thaw_json(normal.evidence),
|
|
1159
|
+
"selected_parent_ids": [
|
|
1160
|
+
parent.candidate_id.value for parent in selected_parents
|
|
1161
|
+
],
|
|
1162
|
+
"selected_parent_configuration_sha256s": [
|
|
1163
|
+
parent.occurrence.configuration_hash
|
|
1164
|
+
for parent in selected_parents
|
|
1165
|
+
],
|
|
1166
|
+
"selected_parents_are_current_front_members": all(
|
|
1167
|
+
parent.candidate_id in front_ids for parent in selected_parents
|
|
1168
|
+
),
|
|
1169
|
+
"provider_fields_consulted": False,
|
|
1170
|
+
"workload_semantics_consulted": False,
|
|
1171
|
+
"objective_names_consulted": False,
|
|
1172
|
+
}
|
|
1173
|
+
),
|
|
1174
|
+
)
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
_RESIDUAL_FRONTIER_PARENT_POLICY_ID = "residual_hypervolume_campaign_parent"
|
|
1178
|
+
_RESIDUAL_FRONTIER_PARENT_POLICY_VERSION = 1
|
|
1179
|
+
_RESIDUAL_FRONTIER_PARENT_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
1180
|
+
b"agent-evolve:residual-hypervolume-campaign-parent:v1;"
|
|
1181
|
+
b"input=current-front-plus-authenticated-affine-prior-archive;"
|
|
1182
|
+
b"target=largest-positive-pairwise-midpoint-hypervolume-residual;"
|
|
1183
|
+
b"parents=distinct-current-front-members-nearest-two-cell-anchors;"
|
|
1184
|
+
b"fallback=stagnation-aware-diverse-parent-when-no-positive-cell;"
|
|
1185
|
+
b"lanes=stable-elite-explorer-identifiers-for-contract-compatibility;"
|
|
1186
|
+
b"current-future-outcomes=false;workload-model-provider-fields=false"
|
|
1187
|
+
).hexdigest()
|
|
1188
|
+
|
|
1189
|
+
|
|
1190
|
+
@dataclass(frozen=True, slots=True)
|
|
1191
|
+
class ResidualHypervolumeCampaignParentSelector:
|
|
1192
|
+
"""Coordinate two parent lanes around the largest missing frontier cell."""
|
|
1193
|
+
|
|
1194
|
+
policy_id = _RESIDUAL_FRONTIER_PARENT_POLICY_ID
|
|
1195
|
+
policy_version = _RESIDUAL_FRONTIER_PARENT_POLICY_VERSION
|
|
1196
|
+
definition_sha256 = _RESIDUAL_FRONTIER_PARENT_POLICY_DEFINITION_SHA256
|
|
1197
|
+
|
|
1198
|
+
def select(
|
|
1199
|
+
self,
|
|
1200
|
+
state: OptimizerState,
|
|
1201
|
+
*,
|
|
1202
|
+
task_sha256: str,
|
|
1203
|
+
parent_count: int,
|
|
1204
|
+
rotation_index: int,
|
|
1205
|
+
progress: tuple[CampaignParentSelectionProgress, ...] = (),
|
|
1206
|
+
archive_utility: ArchiveUtilitySnapshot | None = None,
|
|
1207
|
+
) -> CampaignParentSelection:
|
|
1208
|
+
if type(self) is not ResidualHypervolumeCampaignParentSelector:
|
|
1209
|
+
raise TypeError("selector must be an exact residual-frontier selector")
|
|
1210
|
+
OptimizerState.__post_init__(state)
|
|
1211
|
+
_validate_parent_selection_progress(
|
|
1212
|
+
progress,
|
|
1213
|
+
optimizer_generation=state.generation,
|
|
1214
|
+
)
|
|
1215
|
+
require_sha256(task_sha256, "task_sha256")
|
|
1216
|
+
if type(parent_count) is not int or parent_count != 2:
|
|
1217
|
+
raise ValueError(
|
|
1218
|
+
"residual-frontier campaign parent selection requires two lanes"
|
|
1219
|
+
)
|
|
1220
|
+
if type(rotation_index) is not int or not 0 <= rotation_index < (1 << 63):
|
|
1221
|
+
raise ValueError("rotation_index must be an exact non-negative int63")
|
|
1222
|
+
if type(archive_utility) is not ArchiveUtilitySnapshot:
|
|
1223
|
+
raise TypeError(
|
|
1224
|
+
"residual-frontier parent selection requires archive_utility"
|
|
1225
|
+
)
|
|
1226
|
+
archive_utility.__post_init__()
|
|
1227
|
+
if archive_utility.generation != state.generation + 1:
|
|
1228
|
+
raise ValueError(
|
|
1229
|
+
"residual-frontier archive utility is stale for optimizer state"
|
|
1230
|
+
)
|
|
1231
|
+
|
|
1232
|
+
geometry = residual_frontier_geometry(archive_utility)
|
|
1233
|
+
selected = residual_anchor_parents(
|
|
1234
|
+
geometry=geometry,
|
|
1235
|
+
candidates=state.archive.front_candidates,
|
|
1236
|
+
)
|
|
1237
|
+
if selected is None:
|
|
1238
|
+
fallback = StagnationAwareDiverseCampaignParentSelector().select(
|
|
1239
|
+
state,
|
|
1240
|
+
task_sha256=task_sha256,
|
|
1241
|
+
parent_count=parent_count,
|
|
1242
|
+
rotation_index=rotation_index,
|
|
1243
|
+
progress=progress,
|
|
1244
|
+
archive_utility=archive_utility,
|
|
1245
|
+
)
|
|
1246
|
+
return CampaignParentSelection(
|
|
1247
|
+
parents=fallback.parents,
|
|
1248
|
+
lanes=fallback.lanes,
|
|
1249
|
+
decision_slots=fallback.decision_slots,
|
|
1250
|
+
evidence=_object(
|
|
1251
|
+
{
|
|
1252
|
+
"schema_version": 1,
|
|
1253
|
+
"policy_id": self.policy_id,
|
|
1254
|
+
"policy_version": self.policy_version,
|
|
1255
|
+
"policy_definition_sha256": self.definition_sha256,
|
|
1256
|
+
"residual_geometry_sha256": geometry.geometry_sha256,
|
|
1257
|
+
"residual_cell_count": len(geometry.cells),
|
|
1258
|
+
"source_mode": "no_positive_residual_fallback",
|
|
1259
|
+
"fallback_evidence": thaw_json(fallback.evidence),
|
|
1260
|
+
"current_or_future_candidate_outcomes_consulted": False,
|
|
1261
|
+
"workload_model_provider_fields_consulted": False,
|
|
1262
|
+
}
|
|
1263
|
+
),
|
|
1264
|
+
)
|
|
1265
|
+
|
|
1266
|
+
front_ids = {value.candidate_id for value in state.archive.front_candidates}
|
|
1267
|
+
if any(value.candidate_id not in front_ids for value in selected):
|
|
1268
|
+
raise ValueError("residual-frontier parents escaped the current front")
|
|
1269
|
+
best_cell = geometry.cells[0]
|
|
1270
|
+
lanes = (
|
|
1271
|
+
CampaignParentLane(lane_id="elite", parent=selected[0]),
|
|
1272
|
+
CampaignParentLane(lane_id="explorer", parent=selected[1]),
|
|
1273
|
+
)
|
|
1274
|
+
return CampaignParentSelection(
|
|
1275
|
+
parents=selected,
|
|
1276
|
+
lanes=lanes,
|
|
1277
|
+
evidence=_object(
|
|
1278
|
+
{
|
|
1279
|
+
"schema_version": 1,
|
|
1280
|
+
"policy_id": self.policy_id,
|
|
1281
|
+
"policy_version": self.policy_version,
|
|
1282
|
+
"policy_definition_sha256": self.definition_sha256,
|
|
1283
|
+
"residual_core_definition_sha256": (
|
|
1284
|
+
RESIDUAL_FRONTIER_POLICY_DEFINITION_SHA256
|
|
1285
|
+
),
|
|
1286
|
+
"task_sha256": task_sha256,
|
|
1287
|
+
"optimizer_generation": state.generation,
|
|
1288
|
+
"archive_snapshot_sha256": state.archive_snapshot_hash,
|
|
1289
|
+
"archive_utility_snapshot_sha256": (
|
|
1290
|
+
archive_utility.snapshot_sha256
|
|
1291
|
+
),
|
|
1292
|
+
"rotation_index": rotation_index,
|
|
1293
|
+
"residual_geometry_sha256": geometry.geometry_sha256,
|
|
1294
|
+
"residual_cell_count": len(geometry.cells),
|
|
1295
|
+
"selected_cell": best_cell.to_record(),
|
|
1296
|
+
"selected_parent_ids": [
|
|
1297
|
+
value.candidate_id.value for value in selected
|
|
1298
|
+
],
|
|
1299
|
+
"selected_parent_configuration_sha256s": [
|
|
1300
|
+
value.occurrence.configuration_hash for value in selected
|
|
1301
|
+
],
|
|
1302
|
+
"source_mode": "largest_positive_residual_cell_anchors",
|
|
1303
|
+
"current_or_future_candidate_outcomes_consulted": False,
|
|
1304
|
+
"workload_model_provider_fields_consulted": False,
|
|
1305
|
+
}
|
|
1306
|
+
),
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
|
|
1310
|
+
@dataclass(frozen=True, slots=True)
|
|
1311
|
+
class ArchiveEliteExplorerCampaignParentSelector:
|
|
1312
|
+
"""Bind the generic named elite/explorer lanes to campaign execution.
|
|
1313
|
+
|
|
1314
|
+
The underlying policy always returns exactly two parents: one current-front
|
|
1315
|
+
elite and one best-rank dominated explorer, with explicit distinct-front or
|
|
1316
|
+
singleton fallback. Keeping that fixed cardinality at this adapter makes a
|
|
1317
|
+
campaign's lane semantics impossible to erase by requesting an arbitrary
|
|
1318
|
+
parent count.
|
|
1319
|
+
"""
|
|
1320
|
+
|
|
1321
|
+
policy: TaskKeyedArchiveEliteExplorerParentPolicy = field(
|
|
1322
|
+
default_factory=TaskKeyedArchiveEliteExplorerParentPolicy
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
def __post_init__(self) -> None:
|
|
1326
|
+
if type(self.policy) is not TaskKeyedArchiveEliteExplorerParentPolicy:
|
|
1327
|
+
raise TypeError("policy must be an exact elite/explorer parent policy")
|
|
1328
|
+
|
|
1329
|
+
def select(
|
|
1330
|
+
self,
|
|
1331
|
+
state: OptimizerState,
|
|
1332
|
+
*,
|
|
1333
|
+
task_sha256: str,
|
|
1334
|
+
parent_count: int,
|
|
1335
|
+
rotation_index: int,
|
|
1336
|
+
progress: tuple[CampaignParentSelectionProgress, ...] = (),
|
|
1337
|
+
archive_utility: ArchiveUtilitySnapshot | None = None,
|
|
1338
|
+
) -> CampaignParentSelection:
|
|
1339
|
+
del archive_utility
|
|
1340
|
+
_validate_parent_selection_progress(
|
|
1341
|
+
progress,
|
|
1342
|
+
optimizer_generation=state.generation,
|
|
1343
|
+
)
|
|
1344
|
+
if type(parent_count) is not int or parent_count != 2:
|
|
1345
|
+
raise ValueError(
|
|
1346
|
+
"elite/explorer campaign parent selection requires exactly two lanes"
|
|
1347
|
+
)
|
|
1348
|
+
selected = self.policy.select(
|
|
1349
|
+
state,
|
|
1350
|
+
task_sha256=task_sha256,
|
|
1351
|
+
expected_archive_snapshot_hash=state.archive_snapshot_hash,
|
|
1352
|
+
rotation_index=rotation_index,
|
|
1353
|
+
)
|
|
1354
|
+
lanes = tuple(
|
|
1355
|
+
CampaignParentLane(lane_id=lane_id, parent=parent)
|
|
1356
|
+
for lane_id, parent in zip(
|
|
1357
|
+
("elite", "explorer"),
|
|
1358
|
+
selected.parents,
|
|
1359
|
+
strict=True,
|
|
1360
|
+
)
|
|
1361
|
+
)
|
|
1362
|
+
return CampaignParentSelection(
|
|
1363
|
+
parents=selected.parents,
|
|
1364
|
+
evidence=_object(selected.receipt.to_trace_record()),
|
|
1365
|
+
lanes=lanes,
|
|
1366
|
+
)
|
|
1367
|
+
|
|
1368
|
+
|
|
1369
|
+
@dataclass(frozen=True, slots=True)
|
|
1370
|
+
class CampaignPortfolioWaveContext:
|
|
1371
|
+
"""Complete trusted input to one prompt/card treatment construction."""
|
|
1372
|
+
|
|
1373
|
+
prepared: PreparedEvolutionCampaign
|
|
1374
|
+
stage_request: CampaignStageRequest
|
|
1375
|
+
parent_slot: int
|
|
1376
|
+
parent: EvolutionCandidate
|
|
1377
|
+
variation: ParentVariationBinding
|
|
1378
|
+
evidence_context: FrozenJsonObject
|
|
1379
|
+
evidence_cards: tuple[FrozenJsonObject, ...]
|
|
1380
|
+
memory: FrozenJsonObject
|
|
1381
|
+
parent_measurement: ParentMeasurementBinding | None = None
|
|
1382
|
+
archive_context: CampaignPortfolioArchiveContextProjection | None = None
|
|
1383
|
+
test_eligible_reflections: tuple[tuple[str, FrozenJsonObject], ...] = ()
|
|
1384
|
+
parent_lane: CampaignParentLane | None = None
|
|
1385
|
+
decision_slot: CampaignDecisionSlot | None = None
|
|
1386
|
+
contextual_allocation: ContextualPortfolioAllocationContract | None = None
|
|
1387
|
+
frontier_target: CampaignPortfolioFrontierTarget | None = None
|
|
1388
|
+
|
|
1389
|
+
def __post_init__(self) -> None:
|
|
1390
|
+
if type(self.prepared) is not PreparedEvolutionCampaign:
|
|
1391
|
+
raise TypeError("prepared must be exact")
|
|
1392
|
+
if type(self.stage_request) is not CampaignStageRequest:
|
|
1393
|
+
raise TypeError("stage_request must be exact")
|
|
1394
|
+
if self.stage_request.step.kind is not CampaignGenerationKind.PORTFOLIO:
|
|
1395
|
+
raise ValueError("portfolio context requires a portfolio stage")
|
|
1396
|
+
if type(self.parent_slot) is not int or self.parent_slot < 0:
|
|
1397
|
+
raise ValueError("parent_slot must be non-negative")
|
|
1398
|
+
if type(self.parent) is not EvolutionCandidate:
|
|
1399
|
+
raise TypeError("parent must be exact")
|
|
1400
|
+
if type(self.variation) is not ParentVariationBinding:
|
|
1401
|
+
raise TypeError("variation must be exact")
|
|
1402
|
+
lane = self.parent_lane
|
|
1403
|
+
slot = self.decision_slot
|
|
1404
|
+
if lane is None and slot is None:
|
|
1405
|
+
lane = CampaignParentLane(
|
|
1406
|
+
lane_id=f"parent_{self.parent_slot + 1:04d}",
|
|
1407
|
+
parent=self.parent,
|
|
1408
|
+
)
|
|
1409
|
+
slot = CampaignDecisionSlot(
|
|
1410
|
+
slot_id=f"{lane.lane_id}.primary",
|
|
1411
|
+
lane_id=lane.lane_id,
|
|
1412
|
+
)
|
|
1413
|
+
object.__setattr__(self, "parent_lane", lane)
|
|
1414
|
+
object.__setattr__(self, "decision_slot", slot)
|
|
1415
|
+
elif lane is None or slot is None:
|
|
1416
|
+
raise ValueError("parent_lane and decision_slot must be supplied together")
|
|
1417
|
+
if type(lane) is not CampaignParentLane:
|
|
1418
|
+
raise TypeError("parent_lane must be exact or None")
|
|
1419
|
+
if type(slot) is not CampaignDecisionSlot:
|
|
1420
|
+
raise TypeError("decision_slot must be exact or None")
|
|
1421
|
+
CampaignParentLane.__post_init__(lane)
|
|
1422
|
+
CampaignDecisionSlot.__post_init__(slot)
|
|
1423
|
+
if lane.parent is not self.parent or slot.lane_id != lane.lane_id:
|
|
1424
|
+
raise ValueError("decision slot, parent lane, and parent differ")
|
|
1425
|
+
allocation = self.contextual_allocation
|
|
1426
|
+
if allocation is not None:
|
|
1427
|
+
if type(allocation) is not ContextualPortfolioAllocationContract:
|
|
1428
|
+
raise TypeError("contextual_allocation must be exact or None")
|
|
1429
|
+
allocation.__post_init__()
|
|
1430
|
+
if (
|
|
1431
|
+
allocation.campaign_generation != self.stage_request.step.generation
|
|
1432
|
+
or allocation.slice_id != lane.lane_id
|
|
1433
|
+
or allocation.evaluation_slots != self.prepared.protocol.portfolio_width
|
|
1434
|
+
):
|
|
1435
|
+
raise ValueError("contextual allocation differs from its campaign lane")
|
|
1436
|
+
for name in ("evidence_context", "memory"):
|
|
1437
|
+
value = getattr(self, name)
|
|
1438
|
+
if type(value) is not FrozenJsonObject or freeze_json(value) is not value:
|
|
1439
|
+
raise TypeError(f"{name} must be an exact frozen object")
|
|
1440
|
+
evidence_values = dict(self.evidence_context.items)
|
|
1441
|
+
if CAMPAIGN_SELECTOR_CONTEXT_EXTENSION_KEY in evidence_values:
|
|
1442
|
+
raise ValueError(
|
|
1443
|
+
"trusted evidence context uses the reserved selector extension key"
|
|
1444
|
+
)
|
|
1445
|
+
archive_context = self.archive_context
|
|
1446
|
+
archive_payload = evidence_values.get(CAMPAIGN_ARCHIVE_CONTEXT_KEY)
|
|
1447
|
+
if archive_context is not None:
|
|
1448
|
+
if type(archive_context) is not CampaignPortfolioArchiveContextProjection:
|
|
1449
|
+
raise TypeError("archive_context must be exact or None")
|
|
1450
|
+
archive_context.__post_init__()
|
|
1451
|
+
if (
|
|
1452
|
+
archive_context.archive_utility_snapshot_sha256
|
|
1453
|
+
!= self.stage_request.archive_utility.snapshot_sha256
|
|
1454
|
+
or archive_context.parent_configuration_sha256
|
|
1455
|
+
!= self.parent.occurrence.configuration_hash
|
|
1456
|
+
):
|
|
1457
|
+
raise ValueError("archive context differs from its campaign lane")
|
|
1458
|
+
if (
|
|
1459
|
+
archive_payload is None
|
|
1460
|
+
or thaw_json(archive_payload) != archive_context.to_record()
|
|
1461
|
+
):
|
|
1462
|
+
raise ValueError(
|
|
1463
|
+
"archive context payload differs from its authenticated receipt"
|
|
1464
|
+
)
|
|
1465
|
+
target = self.frontier_target
|
|
1466
|
+
target_payload = evidence_values.get(CAMPAIGN_FRONTIER_TARGET_KEY)
|
|
1467
|
+
if target is None:
|
|
1468
|
+
if target_payload is not None:
|
|
1469
|
+
raise ValueError(
|
|
1470
|
+
"trusted evidence context uses an unauthenticated frontier target"
|
|
1471
|
+
)
|
|
1472
|
+
else:
|
|
1473
|
+
if type(target) is not CampaignPortfolioFrontierTarget:
|
|
1474
|
+
raise TypeError("frontier_target must be exact or None")
|
|
1475
|
+
target.__post_init__()
|
|
1476
|
+
if (
|
|
1477
|
+
target.lane_id != lane.lane_id
|
|
1478
|
+
or target.parent_configuration_sha256
|
|
1479
|
+
!= self.parent.occurrence.configuration_hash
|
|
1480
|
+
or target.archive_utility_snapshot_sha256
|
|
1481
|
+
!= self.stage_request.archive_utility.snapshot_sha256
|
|
1482
|
+
):
|
|
1483
|
+
raise ValueError("frontier target differs from its campaign lane")
|
|
1484
|
+
if (
|
|
1485
|
+
target_payload is None
|
|
1486
|
+
or thaw_json(target_payload) != target.to_record()
|
|
1487
|
+
):
|
|
1488
|
+
raise ValueError(
|
|
1489
|
+
"frontier target payload differs from its authenticated receipt"
|
|
1490
|
+
)
|
|
1491
|
+
if type(self.evidence_cards) is not tuple or not self.evidence_cards:
|
|
1492
|
+
raise ValueError("portfolio context requires evidence cards")
|
|
1493
|
+
if any(type(value) is not FrozenJsonObject for value in self.evidence_cards):
|
|
1494
|
+
raise TypeError("evidence_cards must contain frozen objects")
|
|
1495
|
+
measurement = self.parent_measurement
|
|
1496
|
+
if measurement is not None:
|
|
1497
|
+
if type(measurement) is not ParentMeasurementBinding:
|
|
1498
|
+
raise TypeError("parent_measurement must be exact or None")
|
|
1499
|
+
measurement.__post_init__()
|
|
1500
|
+
occurrence = self.parent.occurrence
|
|
1501
|
+
operator_invocation_id = (
|
|
1502
|
+
None
|
|
1503
|
+
if occurrence.operator_invocation_id is None
|
|
1504
|
+
else occurrence.operator_invocation_id.value
|
|
1505
|
+
)
|
|
1506
|
+
if (
|
|
1507
|
+
measurement.candidate.candidate_id != self.parent.candidate_id.value
|
|
1508
|
+
or measurement.candidate.configuration_sha256
|
|
1509
|
+
!= occurrence.configuration_hash
|
|
1510
|
+
or measurement.candidate.configuration_artifact_sha256
|
|
1511
|
+
!= occurrence.configuration_artifact_hash
|
|
1512
|
+
or measurement.candidate.proposal_sequence
|
|
1513
|
+
!= occurrence.proposal_sequence
|
|
1514
|
+
or measurement.candidate.operator_invocation_id
|
|
1515
|
+
!= operator_invocation_id
|
|
1516
|
+
or measurement.projection.benchmark_sha256
|
|
1517
|
+
!= self.variation.benchmark_sha256
|
|
1518
|
+
or measurement.projection.session_sha256
|
|
1519
|
+
!= self.prepared.benchmark_session.session_sha256
|
|
1520
|
+
):
|
|
1521
|
+
raise ValueError("parent measurement differs from campaign parent")
|
|
1522
|
+
if type(self.test_eligible_reflections) is not tuple:
|
|
1523
|
+
raise TypeError("test_eligible_reflections must be an exact tuple")
|
|
1524
|
+
observed_hashes: list[str] = []
|
|
1525
|
+
for item in self.test_eligible_reflections:
|
|
1526
|
+
if (
|
|
1527
|
+
type(item) is not tuple
|
|
1528
|
+
or len(item) != 2
|
|
1529
|
+
or type(item[0]) is not str
|
|
1530
|
+
or len(item[0]) != 64
|
|
1531
|
+
or type(item[1]) is not FrozenJsonObject
|
|
1532
|
+
):
|
|
1533
|
+
raise TypeError(
|
|
1534
|
+
"test-eligible reflections must contain hash/object pairs"
|
|
1535
|
+
)
|
|
1536
|
+
observed_hashes.append(item[0])
|
|
1537
|
+
if tuple(observed_hashes) != tuple(sorted(set(observed_hashes))):
|
|
1538
|
+
raise ValueError("test-eligible reflection hashes must be canonical")
|
|
1539
|
+
|
|
1540
|
+
|
|
1541
|
+
@runtime_checkable
|
|
1542
|
+
class CampaignPortfolioWaveFactory(Protocol):
|
|
1543
|
+
"""Turn trusted workload evidence into one executable portfolio wave."""
|
|
1544
|
+
|
|
1545
|
+
def build(
|
|
1546
|
+
self,
|
|
1547
|
+
context: CampaignPortfolioWaveContext,
|
|
1548
|
+
) -> PortfolioVariationWaveRequest: ...
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
@runtime_checkable
|
|
1552
|
+
class CampaignPortfolioWaveBatchFactory(Protocol):
|
|
1553
|
+
"""Optionally construct one portfolio stage as an atomic lane cohort.
|
|
1554
|
+
|
|
1555
|
+
Batch construction is required when a workload-neutral policy must solve a
|
|
1556
|
+
constraint across lanes, such as assigning distinct compatible memory
|
|
1557
|
+
cards. The ordinary single-wave factory remains the default compatibility
|
|
1558
|
+
surface; runtimes opt into this protocol structurally through
|
|
1559
|
+
``build_batch``.
|
|
1560
|
+
"""
|
|
1561
|
+
|
|
1562
|
+
def build_batch(
|
|
1563
|
+
self,
|
|
1564
|
+
contexts: tuple[CampaignPortfolioWaveContext, ...],
|
|
1565
|
+
) -> tuple[PortfolioVariationWaveRequest, ...]: ...
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
@runtime_checkable
|
|
1569
|
+
class CampaignPortfolioContextEnricher(Protocol):
|
|
1570
|
+
"""Retrieve parent/lane-local history immediately before wave building.
|
|
1571
|
+
|
|
1572
|
+
The input is the complete authenticated base context. Implementations may
|
|
1573
|
+
query contextual outcome memory, but return only a bounded frozen history
|
|
1574
|
+
payload. The runtime attaches it under a reserved key, preserving every
|
|
1575
|
+
workload- and core-authored base-context field exactly.
|
|
1576
|
+
"""
|
|
1577
|
+
|
|
1578
|
+
def enrich(
|
|
1579
|
+
self,
|
|
1580
|
+
context: CampaignPortfolioWaveContext,
|
|
1581
|
+
) -> FrozenJsonObject: ...
|
|
1582
|
+
|
|
1583
|
+
|
|
1584
|
+
@dataclass(frozen=True, slots=True)
|
|
1585
|
+
class CampaignPortfolioMemoryEstimandProjection:
|
|
1586
|
+
"""Atomic replacement for the core-owned memory-estimand context pair.
|
|
1587
|
+
|
|
1588
|
+
The campaign runtime, rather than a workload wave factory, installs this
|
|
1589
|
+
projection into the trusted selector context. The digest is deliberately
|
|
1590
|
+
redundant: it binds a diagnostic stratum to the exact typed-JSON estimand
|
|
1591
|
+
object and prevents a projector from publishing a mismatched pair.
|
|
1592
|
+
"""
|
|
1593
|
+
|
|
1594
|
+
estimand_context: FrozenJsonObject
|
|
1595
|
+
estimand_stratum_sha256: str
|
|
1596
|
+
|
|
1597
|
+
def __post_init__(self) -> None:
|
|
1598
|
+
if (
|
|
1599
|
+
type(self.estimand_context) is not FrozenJsonObject
|
|
1600
|
+
or freeze_json(self.estimand_context) is not self.estimand_context
|
|
1601
|
+
):
|
|
1602
|
+
raise TypeError("estimand_context must be an exact frozen object")
|
|
1603
|
+
_require_sha256(
|
|
1604
|
+
self.estimand_stratum_sha256,
|
|
1605
|
+
"estimand_stratum_sha256",
|
|
1606
|
+
)
|
|
1607
|
+
if typed_json_sha256(self.estimand_context) != self.estimand_stratum_sha256:
|
|
1608
|
+
raise ValueError(
|
|
1609
|
+
"estimand_stratum_sha256 must identify the exact estimand context"
|
|
1610
|
+
)
|
|
1611
|
+
|
|
1612
|
+
|
|
1613
|
+
@runtime_checkable
|
|
1614
|
+
class CampaignPortfolioMemoryEstimandProjector(Protocol):
|
|
1615
|
+
"""Project a prospective wave onto an authenticated memory estimand.
|
|
1616
|
+
|
|
1617
|
+
Returning ``None`` preserves the workload-authored selector context. A
|
|
1618
|
+
projection authorizes replacement of exactly the core-owned estimand
|
|
1619
|
+
object and matching stratum digest; every other context field is retained
|
|
1620
|
+
byte-for-byte by the campaign runtime.
|
|
1621
|
+
"""
|
|
1622
|
+
|
|
1623
|
+
def project(
|
|
1624
|
+
self,
|
|
1625
|
+
context: CampaignPortfolioWaveContext,
|
|
1626
|
+
) -> CampaignPortfolioMemoryEstimandProjection | None: ...
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
@dataclass(frozen=True, slots=True)
|
|
1630
|
+
class CampaignPortfolioWavePreparationReceipt:
|
|
1631
|
+
"""Content-free evidence that one trusted wave was ready for dispatch.
|
|
1632
|
+
|
|
1633
|
+
This receipt is emitted after the wave factory's output has passed the
|
|
1634
|
+
campaign trust-boundary validation and before any selector/provider work
|
|
1635
|
+
starts. It deliberately records prompt-adjacent objects only through
|
|
1636
|
+
their existing canonical hashes. ``card_records`` are the public
|
|
1637
|
+
:class:`PortfolioCard` audit records, which contain prompt-view hashes but
|
|
1638
|
+
never the prompt payload itself.
|
|
1639
|
+
|
|
1640
|
+
A failed stage is not a sealed scientific result. The receipt instead
|
|
1641
|
+
preserves the prospective treatment, projection, and lineage needed to
|
|
1642
|
+
audit why that unsealed stage would have dispatched the cards it did.
|
|
1643
|
+
"""
|
|
1644
|
+
|
|
1645
|
+
campaign_preparation_sha256: str
|
|
1646
|
+
stage_request_sha256: str
|
|
1647
|
+
stage_preparation_sha256: str
|
|
1648
|
+
generation: int
|
|
1649
|
+
parent_slot: int
|
|
1650
|
+
parent_lane_id: str
|
|
1651
|
+
decision_slot_id: str
|
|
1652
|
+
parent_candidate_id: str
|
|
1653
|
+
parent_configuration_sha256: str
|
|
1654
|
+
parent_configuration_artifact_sha256: str
|
|
1655
|
+
finite_contract_identity_sha256: str
|
|
1656
|
+
selector_call_id: str
|
|
1657
|
+
selector_request_sha256: str
|
|
1658
|
+
workload_context_sha256: str
|
|
1659
|
+
pre_memory_projection_context_sha256: str
|
|
1660
|
+
selector_context_sha256: str
|
|
1661
|
+
evidence_card_snapshot_sha256: str
|
|
1662
|
+
selector_card_snapshot_sha256: str
|
|
1663
|
+
card_records: tuple[FrozenJsonObject, ...]
|
|
1664
|
+
card_reference_mapping: tuple[FrozenJsonObject, ...]
|
|
1665
|
+
test_eligible_reflection_receipts: tuple[FrozenJsonObject, ...]
|
|
1666
|
+
context_projection_identity: FrozenJsonObject
|
|
1667
|
+
memory_credit_identity: FrozenJsonObject | None
|
|
1668
|
+
receipt_sha256: str = field(init=False)
|
|
1669
|
+
|
|
1670
|
+
def __post_init__(self) -> None:
|
|
1671
|
+
for name in (
|
|
1672
|
+
"campaign_preparation_sha256",
|
|
1673
|
+
"stage_request_sha256",
|
|
1674
|
+
"stage_preparation_sha256",
|
|
1675
|
+
"parent_configuration_sha256",
|
|
1676
|
+
"parent_configuration_artifact_sha256",
|
|
1677
|
+
"finite_contract_identity_sha256",
|
|
1678
|
+
"selector_request_sha256",
|
|
1679
|
+
"workload_context_sha256",
|
|
1680
|
+
"pre_memory_projection_context_sha256",
|
|
1681
|
+
"selector_context_sha256",
|
|
1682
|
+
"evidence_card_snapshot_sha256",
|
|
1683
|
+
"selector_card_snapshot_sha256",
|
|
1684
|
+
):
|
|
1685
|
+
_require_sha256(getattr(self, name), name)
|
|
1686
|
+
if type(self.generation) is not int or self.generation <= 0:
|
|
1687
|
+
raise ValueError("generation must be a positive exact integer")
|
|
1688
|
+
if type(self.parent_slot) is not int or self.parent_slot < 0:
|
|
1689
|
+
raise ValueError("parent_slot must be a non-negative exact integer")
|
|
1690
|
+
_require_role_token(self.parent_lane_id, "parent_lane_id")
|
|
1691
|
+
_require_role_token(self.decision_slot_id, "decision_slot_id")
|
|
1692
|
+
for name in ("parent_candidate_id", "selector_call_id"):
|
|
1693
|
+
value = getattr(self, name)
|
|
1694
|
+
if type(value) is not str or not value:
|
|
1695
|
+
raise ValueError(f"{name} must be a non-empty exact string")
|
|
1696
|
+
for name in (
|
|
1697
|
+
"card_records",
|
|
1698
|
+
"card_reference_mapping",
|
|
1699
|
+
"test_eligible_reflection_receipts",
|
|
1700
|
+
):
|
|
1701
|
+
values = getattr(self, name)
|
|
1702
|
+
if type(values) is not tuple or any(
|
|
1703
|
+
type(value) is not FrozenJsonObject or freeze_json(value) is not value
|
|
1704
|
+
for value in values
|
|
1705
|
+
):
|
|
1706
|
+
raise TypeError(f"{name} must contain exact frozen objects")
|
|
1707
|
+
if not self.card_records:
|
|
1708
|
+
raise ValueError("a prepared portfolio wave requires card records")
|
|
1709
|
+
if len(self.card_records) != len(self.card_reference_mapping):
|
|
1710
|
+
raise ValueError("card records and reference mapping differ")
|
|
1711
|
+
card_keys = tuple(
|
|
1712
|
+
thaw_json(value).get("card_key") for value in self.card_records
|
|
1713
|
+
)
|
|
1714
|
+
mapped_keys = tuple(
|
|
1715
|
+
thaw_json(value).get("card_key") for value in self.card_reference_mapping
|
|
1716
|
+
)
|
|
1717
|
+
if card_keys != mapped_keys or card_keys != tuple(sorted(set(card_keys))):
|
|
1718
|
+
raise ValueError("card records and references require canonical card keys")
|
|
1719
|
+
reflection_receipts = tuple(
|
|
1720
|
+
thaw_json(value).get("reflection_receipt_sha256")
|
|
1721
|
+
for value in self.test_eligible_reflection_receipts
|
|
1722
|
+
)
|
|
1723
|
+
if reflection_receipts != tuple(sorted(set(reflection_receipts))):
|
|
1724
|
+
raise ValueError("test-eligible reflection receipts must be canonical")
|
|
1725
|
+
for value in self.test_eligible_reflection_receipts:
|
|
1726
|
+
record = thaw_json(value)
|
|
1727
|
+
_require_sha256(
|
|
1728
|
+
record.get("reflection_receipt_sha256"),
|
|
1729
|
+
"reflection_receipt_sha256",
|
|
1730
|
+
)
|
|
1731
|
+
_require_sha256(
|
|
1732
|
+
record.get("reflection_result_sha256"),
|
|
1733
|
+
"reflection_result_sha256",
|
|
1734
|
+
)
|
|
1735
|
+
for name in ("context_projection_identity", "memory_credit_identity"):
|
|
1736
|
+
value = getattr(self, name)
|
|
1737
|
+
if value is not None and (
|
|
1738
|
+
type(value) is not FrozenJsonObject or freeze_json(value) is not value
|
|
1739
|
+
):
|
|
1740
|
+
raise TypeError(f"{name} must be an exact frozen object or None")
|
|
1741
|
+
context_projection = thaw_json(self.context_projection_identity)
|
|
1742
|
+
if (
|
|
1743
|
+
context_projection.get("selector_context_sha256")
|
|
1744
|
+
!= self.selector_context_sha256
|
|
1745
|
+
):
|
|
1746
|
+
raise ValueError("context projection names a different selector context")
|
|
1747
|
+
object.__setattr__(
|
|
1748
|
+
self,
|
|
1749
|
+
"receipt_sha256",
|
|
1750
|
+
_preparation_sha256(
|
|
1751
|
+
_WAVE_PREPARATION_DOMAIN,
|
|
1752
|
+
_object(self._unsigned_record()),
|
|
1753
|
+
),
|
|
1754
|
+
)
|
|
1755
|
+
|
|
1756
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1757
|
+
return {
|
|
1758
|
+
"schema_version": 1,
|
|
1759
|
+
"campaign_preparation_sha256": self.campaign_preparation_sha256,
|
|
1760
|
+
"stage_request_sha256": self.stage_request_sha256,
|
|
1761
|
+
"stage_preparation_sha256": self.stage_preparation_sha256,
|
|
1762
|
+
"generation": self.generation,
|
|
1763
|
+
"parent_slot": self.parent_slot,
|
|
1764
|
+
"parent_lane_id": self.parent_lane_id,
|
|
1765
|
+
"decision_slot_id": self.decision_slot_id,
|
|
1766
|
+
"parent_candidate_id": self.parent_candidate_id,
|
|
1767
|
+
"parent_configuration_sha256": self.parent_configuration_sha256,
|
|
1768
|
+
"parent_configuration_artifact_sha256": (
|
|
1769
|
+
self.parent_configuration_artifact_sha256
|
|
1770
|
+
),
|
|
1771
|
+
"finite_contract_identity_sha256": (self.finite_contract_identity_sha256),
|
|
1772
|
+
"selector_call_id": self.selector_call_id,
|
|
1773
|
+
"selector_request_sha256": self.selector_request_sha256,
|
|
1774
|
+
"workload_context_sha256": self.workload_context_sha256,
|
|
1775
|
+
"pre_memory_projection_context_sha256": (
|
|
1776
|
+
self.pre_memory_projection_context_sha256
|
|
1777
|
+
),
|
|
1778
|
+
"selector_context_sha256": self.selector_context_sha256,
|
|
1779
|
+
"evidence_card_snapshot_sha256": self.evidence_card_snapshot_sha256,
|
|
1780
|
+
"selector_card_snapshot_sha256": self.selector_card_snapshot_sha256,
|
|
1781
|
+
"card_records": [thaw_json(value) for value in self.card_records],
|
|
1782
|
+
"card_reference_mapping": [
|
|
1783
|
+
thaw_json(value) for value in self.card_reference_mapping
|
|
1784
|
+
],
|
|
1785
|
+
"test_eligible_reflection_receipts": [
|
|
1786
|
+
thaw_json(value) for value in self.test_eligible_reflection_receipts
|
|
1787
|
+
],
|
|
1788
|
+
"context_projection_identity": thaw_json(self.context_projection_identity),
|
|
1789
|
+
"memory_credit_identity": (
|
|
1790
|
+
None
|
|
1791
|
+
if self.memory_credit_identity is None
|
|
1792
|
+
else thaw_json(self.memory_credit_identity)
|
|
1793
|
+
),
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
def to_record(self) -> dict[str, object]:
|
|
1797
|
+
self.__post_init__()
|
|
1798
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
1799
|
+
|
|
1800
|
+
|
|
1801
|
+
@runtime_checkable
|
|
1802
|
+
class CampaignPortfolioWavePreparationObserver(Protocol):
|
|
1803
|
+
"""Durably record a validated prospective wave before dispatch.
|
|
1804
|
+
|
|
1805
|
+
Implementations must finish durable publication before returning. Raising
|
|
1806
|
+
aborts the stage before any selector call is submitted. The observer is
|
|
1807
|
+
intentionally separate from campaign stage receipts because a failed or
|
|
1808
|
+
cancelled stage never has a sealed :class:`CampaignStageReceipt`.
|
|
1809
|
+
"""
|
|
1810
|
+
|
|
1811
|
+
def record_prepared_wave(
|
|
1812
|
+
self,
|
|
1813
|
+
receipt: CampaignPortfolioWavePreparationReceipt,
|
|
1814
|
+
) -> None: ...
|
|
1815
|
+
|
|
1816
|
+
|
|
1817
|
+
def _validated_wave_preparation_receipt(
|
|
1818
|
+
*,
|
|
1819
|
+
build: CampaignPortfolioWaveContext,
|
|
1820
|
+
wave: PortfolioVariationWaveRequest,
|
|
1821
|
+
workload_context_sha256: str,
|
|
1822
|
+
pre_memory_projection_context_sha256: str,
|
|
1823
|
+
) -> CampaignPortfolioWavePreparationReceipt:
|
|
1824
|
+
"""Project an already validated wave into content-free audit evidence."""
|
|
1825
|
+
|
|
1826
|
+
request = wave.selection_request
|
|
1827
|
+
credit = wave.memory_credit
|
|
1828
|
+
matched = wave.matched_memory_control
|
|
1829
|
+
if credit is None and matched is None:
|
|
1830
|
+
context_values = dict(request.context.items)
|
|
1831
|
+
context_projection = (
|
|
1832
|
+
PortfolioMemoryContextProjectionBinding.from_selector_context(
|
|
1833
|
+
request.context
|
|
1834
|
+
)
|
|
1835
|
+
if MEMORY_ESTIMAND_CONTEXT_KEY in context_values
|
|
1836
|
+
else PortfolioMemoryContextProjectionBinding.exact_identity(
|
|
1837
|
+
request.context_sha256
|
|
1838
|
+
)
|
|
1839
|
+
)
|
|
1840
|
+
memory_credit_identity = None
|
|
1841
|
+
elif credit is not None:
|
|
1842
|
+
context_projection = credit.resolve_context_projection(request.context)
|
|
1843
|
+
admission = credit.quarantine_admission
|
|
1844
|
+
memory_credit_identity = _object(
|
|
1845
|
+
{
|
|
1846
|
+
"schema_version": 1,
|
|
1847
|
+
"credit_unit_id": credit.credit_unit_id.value,
|
|
1848
|
+
"treatment_binding_sha256": credit.treatment_binding_sha256,
|
|
1849
|
+
"selection_decision_sha256": (
|
|
1850
|
+
credit.assignment.selection_decision_sha256
|
|
1851
|
+
),
|
|
1852
|
+
"assignment_sha256": credit.assignment.assignment_sha256,
|
|
1853
|
+
"score_snapshot_sha256": credit.score_snapshot.snapshot_sha256,
|
|
1854
|
+
"aggregation_binding_sha256": credit.aggregation.binding_sha256,
|
|
1855
|
+
"card_source_registry_sha256": (credit.card_source_registry_sha256),
|
|
1856
|
+
"context_projection_binding_sha256": (
|
|
1857
|
+
context_projection.binding_sha256
|
|
1858
|
+
),
|
|
1859
|
+
"quarantine_admission": (
|
|
1860
|
+
None
|
|
1861
|
+
if admission is None
|
|
1862
|
+
else {
|
|
1863
|
+
"receipt_sha256": admission.receipt_sha256,
|
|
1864
|
+
"source_admission_request_sha256": (
|
|
1865
|
+
admission.source_admission_request_sha256
|
|
1866
|
+
),
|
|
1867
|
+
"references": [
|
|
1868
|
+
{
|
|
1869
|
+
"insight_id": value.insight_id.value,
|
|
1870
|
+
"version": value.version,
|
|
1871
|
+
}
|
|
1872
|
+
for value in admission.references
|
|
1873
|
+
],
|
|
1874
|
+
}
|
|
1875
|
+
),
|
|
1876
|
+
"quarantine_admission_subset_authorization_sha256": (
|
|
1877
|
+
credit.quarantine_admission_subset_authorization_sha256
|
|
1878
|
+
),
|
|
1879
|
+
}
|
|
1880
|
+
)
|
|
1881
|
+
else:
|
|
1882
|
+
assert matched is not None
|
|
1883
|
+
context_projection = matched.context_projection
|
|
1884
|
+
context_projection.replay(request.context)
|
|
1885
|
+
parent_lane = build.parent_lane
|
|
1886
|
+
if parent_lane is None:
|
|
1887
|
+
raise ValueError("matched memory arm requires a stable campaign lane")
|
|
1888
|
+
if (
|
|
1889
|
+
matched.assignment.unit.generation != wave.generation
|
|
1890
|
+
or matched.assignment.unit.lane_id != parent_lane.lane_id
|
|
1891
|
+
):
|
|
1892
|
+
raise ValueError("matched memory assignment differs from the campaign lane")
|
|
1893
|
+
memory_credit_identity = _object(
|
|
1894
|
+
{
|
|
1895
|
+
"schema_version": 2,
|
|
1896
|
+
"evidence_kind": "randomized_active_neutral_arm",
|
|
1897
|
+
"plan": matched.plan.to_record(),
|
|
1898
|
+
"assignment": matched.assignment.to_record(),
|
|
1899
|
+
"arm_view": matched.arm_view.to_record(),
|
|
1900
|
+
"aggregation": {
|
|
1901
|
+
**matched.aggregation.to_record(),
|
|
1902
|
+
"binding_sha256": matched.aggregation.binding_sha256,
|
|
1903
|
+
},
|
|
1904
|
+
"context_projection_binding_sha256": (
|
|
1905
|
+
context_projection.binding_sha256
|
|
1906
|
+
),
|
|
1907
|
+
"card_vs_neutral_effect_identified": False,
|
|
1908
|
+
"online_score_update_allowed": False,
|
|
1909
|
+
}
|
|
1910
|
+
)
|
|
1911
|
+
context_projection_record: dict[str, object] = {
|
|
1912
|
+
**context_projection.to_record(),
|
|
1913
|
+
"binding_sha256": context_projection.binding_sha256,
|
|
1914
|
+
}
|
|
1915
|
+
selector_context_values = dict(request.context.items)
|
|
1916
|
+
selector_context_extension = resolve_campaign_selector_context_extension(
|
|
1917
|
+
trusted_context=build.evidence_context,
|
|
1918
|
+
selector_context=request.context,
|
|
1919
|
+
)
|
|
1920
|
+
if selector_context_extension is not None:
|
|
1921
|
+
context_projection_record["selector_context_extension"] = {
|
|
1922
|
+
"reserved_key": CAMPAIGN_SELECTOR_CONTEXT_EXTENSION_KEY,
|
|
1923
|
+
**selector_context_extension.to_binding_record(),
|
|
1924
|
+
}
|
|
1925
|
+
archive_context = selector_context_values.get(CAMPAIGN_ARCHIVE_CONTEXT_KEY)
|
|
1926
|
+
if archive_context is not None:
|
|
1927
|
+
if type(archive_context) is not FrozenJsonObject:
|
|
1928
|
+
raise TypeError("reserved campaign archive context must be a frozen object")
|
|
1929
|
+
context_projection_record["archive_context_projection"] = {
|
|
1930
|
+
"reserved_key": CAMPAIGN_ARCHIVE_CONTEXT_KEY,
|
|
1931
|
+
"projection_sha256": typed_json_sha256(archive_context),
|
|
1932
|
+
}
|
|
1933
|
+
frontier_target = selector_context_values.get(CAMPAIGN_FRONTIER_TARGET_KEY)
|
|
1934
|
+
if frontier_target is not None:
|
|
1935
|
+
if type(frontier_target) is not FrozenJsonObject:
|
|
1936
|
+
raise TypeError("reserved campaign frontier target must be frozen")
|
|
1937
|
+
if build.frontier_target is None:
|
|
1938
|
+
raise ValueError("selector context has an unauthenticated frontier target")
|
|
1939
|
+
if thaw_json(frontier_target) != build.frontier_target.to_record():
|
|
1940
|
+
raise ValueError("selector frontier target differs from its receipt")
|
|
1941
|
+
context_projection_record["frontier_target_projection"] = {
|
|
1942
|
+
"reserved_key": CAMPAIGN_FRONTIER_TARGET_KEY,
|
|
1943
|
+
"target_sha256": build.frontier_target.target_sha256,
|
|
1944
|
+
"projection_sha256": typed_json_sha256(frontier_target),
|
|
1945
|
+
}
|
|
1946
|
+
context_projection_identity = _object(context_projection_record)
|
|
1947
|
+
card_records = tuple(_object(card.to_record()) for card in request.cards)
|
|
1948
|
+
card_reference_mapping = tuple(
|
|
1949
|
+
_object(
|
|
1950
|
+
{
|
|
1951
|
+
"card_key": card.card_key,
|
|
1952
|
+
"reference": {
|
|
1953
|
+
"insight_id": card.reference.insight_id.value,
|
|
1954
|
+
"version": card.reference.version,
|
|
1955
|
+
},
|
|
1956
|
+
"source_binding_sha256": (
|
|
1957
|
+
None
|
|
1958
|
+
if card.source_binding is None
|
|
1959
|
+
else card.source_binding.binding_sha256
|
|
1960
|
+
),
|
|
1961
|
+
"derived_view_receipt_sha256": (
|
|
1962
|
+
None
|
|
1963
|
+
if card.derived_view_receipt is None
|
|
1964
|
+
else card.derived_view_receipt.receipt_sha256
|
|
1965
|
+
),
|
|
1966
|
+
}
|
|
1967
|
+
)
|
|
1968
|
+
for card in request.cards
|
|
1969
|
+
)
|
|
1970
|
+
test_eligible_reflections = tuple(
|
|
1971
|
+
_object(
|
|
1972
|
+
{
|
|
1973
|
+
"reflection_receipt_sha256": receipt_sha256,
|
|
1974
|
+
"reflection_result_sha256": typed_json_sha256(result),
|
|
1975
|
+
}
|
|
1976
|
+
)
|
|
1977
|
+
for receipt_sha256, result in build.test_eligible_reflections
|
|
1978
|
+
)
|
|
1979
|
+
evidence_card_snapshot_sha256 = typed_json_sha256(
|
|
1980
|
+
_object(
|
|
1981
|
+
{
|
|
1982
|
+
"schema_version": 1,
|
|
1983
|
+
"cards": [thaw_json(value) for value in build.evidence_cards],
|
|
1984
|
+
}
|
|
1985
|
+
)
|
|
1986
|
+
)
|
|
1987
|
+
parent_lane = build.parent_lane
|
|
1988
|
+
decision_slot = build.decision_slot
|
|
1989
|
+
assert parent_lane is not None
|
|
1990
|
+
assert decision_slot is not None
|
|
1991
|
+
occurrence = build.parent.occurrence
|
|
1992
|
+
return CampaignPortfolioWavePreparationReceipt(
|
|
1993
|
+
campaign_preparation_sha256=build.prepared.preparation_sha256,
|
|
1994
|
+
stage_request_sha256=build.stage_request.request_sha256,
|
|
1995
|
+
stage_preparation_sha256=build.stage_request.preparation_sha256,
|
|
1996
|
+
generation=wave.generation,
|
|
1997
|
+
parent_slot=build.parent_slot,
|
|
1998
|
+
parent_lane_id=parent_lane.lane_id,
|
|
1999
|
+
decision_slot_id=decision_slot.slot_id,
|
|
2000
|
+
parent_candidate_id=build.parent.candidate_id.value,
|
|
2001
|
+
parent_configuration_sha256=occurrence.configuration_hash,
|
|
2002
|
+
parent_configuration_artifact_sha256=(occurrence.configuration_artifact_hash),
|
|
2003
|
+
finite_contract_identity_sha256=(
|
|
2004
|
+
request.finite_variation_contract.identity_sha256
|
|
2005
|
+
),
|
|
2006
|
+
selector_call_id=request.call_id.value,
|
|
2007
|
+
selector_request_sha256=request.request_sha256,
|
|
2008
|
+
workload_context_sha256=workload_context_sha256,
|
|
2009
|
+
pre_memory_projection_context_sha256=(pre_memory_projection_context_sha256),
|
|
2010
|
+
selector_context_sha256=request.context_sha256,
|
|
2011
|
+
evidence_card_snapshot_sha256=evidence_card_snapshot_sha256,
|
|
2012
|
+
selector_card_snapshot_sha256=request.card_snapshot_sha256,
|
|
2013
|
+
card_records=card_records,
|
|
2014
|
+
card_reference_mapping=card_reference_mapping,
|
|
2015
|
+
test_eligible_reflection_receipts=test_eligible_reflections,
|
|
2016
|
+
context_projection_identity=context_projection_identity,
|
|
2017
|
+
memory_credit_identity=memory_credit_identity,
|
|
2018
|
+
)
|
|
2019
|
+
|
|
2020
|
+
|
|
2021
|
+
@dataclass(frozen=True, slots=True)
|
|
2022
|
+
class CampaignPortfolioOutcomePreparation:
|
|
2023
|
+
"""Pure workload-memory projection awaiting a campaign commit barrier."""
|
|
2024
|
+
|
|
2025
|
+
request_sha256: str
|
|
2026
|
+
generation: int
|
|
2027
|
+
wave_request_sha256s: tuple[str, ...]
|
|
2028
|
+
result_receipt_sha256s: tuple[str, ...]
|
|
2029
|
+
prior_memory_sha256: str
|
|
2030
|
+
updated_memory: FrozenJsonObject
|
|
2031
|
+
evidence: FrozenJsonObject
|
|
2032
|
+
preparation_sha256: str = field(init=False)
|
|
2033
|
+
|
|
2034
|
+
def __post_init__(self) -> None:
|
|
2035
|
+
_require_sha256(self.request_sha256, "request_sha256")
|
|
2036
|
+
if type(self.generation) is not int or self.generation <= 0:
|
|
2037
|
+
raise ValueError("generation must be a positive exact integer")
|
|
2038
|
+
for name in ("wave_request_sha256s", "result_receipt_sha256s"):
|
|
2039
|
+
values = getattr(self, name)
|
|
2040
|
+
if type(values) is not tuple or not values:
|
|
2041
|
+
raise ValueError(f"{name} must be a non-empty exact tuple")
|
|
2042
|
+
for value in values:
|
|
2043
|
+
_require_sha256(value, name)
|
|
2044
|
+
if len(self.wave_request_sha256s) != len(self.result_receipt_sha256s):
|
|
2045
|
+
raise ValueError("outcome preparation waves and results differ")
|
|
2046
|
+
_require_sha256(self.prior_memory_sha256, "prior_memory_sha256")
|
|
2047
|
+
for name in ("updated_memory", "evidence"):
|
|
2048
|
+
value = getattr(self, name)
|
|
2049
|
+
if type(value) is not FrozenJsonObject or freeze_json(value) is not value:
|
|
2050
|
+
raise TypeError(f"{name} must be an exact frozen object")
|
|
2051
|
+
object.__setattr__(
|
|
2052
|
+
self,
|
|
2053
|
+
"preparation_sha256",
|
|
2054
|
+
_preparation_sha256(
|
|
2055
|
+
_OUTCOME_PREPARATION_DOMAIN,
|
|
2056
|
+
_object(
|
|
2057
|
+
{
|
|
2058
|
+
"schema_version": 1,
|
|
2059
|
+
"request_sha256": self.request_sha256,
|
|
2060
|
+
"generation": self.generation,
|
|
2061
|
+
"wave_request_sha256s": list(self.wave_request_sha256s),
|
|
2062
|
+
"result_receipt_sha256s": list(self.result_receipt_sha256s),
|
|
2063
|
+
"prior_memory_sha256": self.prior_memory_sha256,
|
|
2064
|
+
"updated_memory_sha256": typed_json_sha256(self.updated_memory),
|
|
2065
|
+
"evidence_sha256": typed_json_sha256(self.evidence),
|
|
2066
|
+
}
|
|
2067
|
+
),
|
|
2068
|
+
),
|
|
2069
|
+
)
|
|
2070
|
+
|
|
2071
|
+
def to_record(self) -> dict[str, object]:
|
|
2072
|
+
self.__post_init__()
|
|
2073
|
+
return {
|
|
2074
|
+
"schema_version": 1,
|
|
2075
|
+
"request_sha256": self.request_sha256,
|
|
2076
|
+
"generation": self.generation,
|
|
2077
|
+
"wave_request_sha256s": list(self.wave_request_sha256s),
|
|
2078
|
+
"result_receipt_sha256s": list(self.result_receipt_sha256s),
|
|
2079
|
+
"prior_memory_sha256": self.prior_memory_sha256,
|
|
2080
|
+
"updated_memory_sha256": typed_json_sha256(self.updated_memory),
|
|
2081
|
+
"evidence": thaw_json(self.evidence),
|
|
2082
|
+
"preparation_sha256": self.preparation_sha256,
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
|
|
2086
|
+
@dataclass(frozen=True, slots=True)
|
|
2087
|
+
class CampaignPortfolioLearningPreparation:
|
|
2088
|
+
"""Pure lifecycle adjudication awaiting a campaign commit barrier."""
|
|
2089
|
+
|
|
2090
|
+
request_sha256: str
|
|
2091
|
+
generation: int
|
|
2092
|
+
wave_request_sha256s: tuple[str, ...]
|
|
2093
|
+
result_receipt_sha256s: tuple[str, ...]
|
|
2094
|
+
memory_credit_preparation_sha256: str
|
|
2095
|
+
evidence: FrozenJsonObject
|
|
2096
|
+
preparation_sha256: str = field(init=False)
|
|
2097
|
+
|
|
2098
|
+
def __post_init__(self) -> None:
|
|
2099
|
+
_require_sha256(self.request_sha256, "request_sha256")
|
|
2100
|
+
if type(self.generation) is not int or self.generation <= 0:
|
|
2101
|
+
raise ValueError("generation must be a positive exact integer")
|
|
2102
|
+
for name in ("wave_request_sha256s", "result_receipt_sha256s"):
|
|
2103
|
+
values = getattr(self, name)
|
|
2104
|
+
if type(values) is not tuple or not values:
|
|
2105
|
+
raise ValueError(f"{name} must be a non-empty exact tuple")
|
|
2106
|
+
for value in values:
|
|
2107
|
+
_require_sha256(value, name)
|
|
2108
|
+
if len(self.wave_request_sha256s) != len(self.result_receipt_sha256s):
|
|
2109
|
+
raise ValueError("learning preparation waves and results differ")
|
|
2110
|
+
_require_sha256(
|
|
2111
|
+
self.memory_credit_preparation_sha256,
|
|
2112
|
+
"memory_credit_preparation_sha256",
|
|
2113
|
+
)
|
|
2114
|
+
if (
|
|
2115
|
+
type(self.evidence) is not FrozenJsonObject
|
|
2116
|
+
or freeze_json(self.evidence) is not self.evidence
|
|
2117
|
+
):
|
|
2118
|
+
raise TypeError("evidence must be an exact frozen object")
|
|
2119
|
+
object.__setattr__(
|
|
2120
|
+
self,
|
|
2121
|
+
"preparation_sha256",
|
|
2122
|
+
_preparation_sha256(
|
|
2123
|
+
_LEARNING_PREPARATION_DOMAIN,
|
|
2124
|
+
_object(
|
|
2125
|
+
{
|
|
2126
|
+
"schema_version": 1,
|
|
2127
|
+
"request_sha256": self.request_sha256,
|
|
2128
|
+
"generation": self.generation,
|
|
2129
|
+
"wave_request_sha256s": list(self.wave_request_sha256s),
|
|
2130
|
+
"result_receipt_sha256s": list(self.result_receipt_sha256s),
|
|
2131
|
+
"memory_credit_preparation_sha256": (
|
|
2132
|
+
self.memory_credit_preparation_sha256
|
|
2133
|
+
),
|
|
2134
|
+
"evidence_sha256": typed_json_sha256(self.evidence),
|
|
2135
|
+
}
|
|
2136
|
+
),
|
|
2137
|
+
),
|
|
2138
|
+
)
|
|
2139
|
+
|
|
2140
|
+
def to_record(self) -> dict[str, object]:
|
|
2141
|
+
self.__post_init__()
|
|
2142
|
+
return {
|
|
2143
|
+
"schema_version": 1,
|
|
2144
|
+
"request_sha256": self.request_sha256,
|
|
2145
|
+
"generation": self.generation,
|
|
2146
|
+
"wave_request_sha256s": list(self.wave_request_sha256s),
|
|
2147
|
+
"result_receipt_sha256s": list(self.result_receipt_sha256s),
|
|
2148
|
+
"memory_credit_preparation_sha256": (self.memory_credit_preparation_sha256),
|
|
2149
|
+
"evidence": thaw_json(self.evidence),
|
|
2150
|
+
"preparation_sha256": self.preparation_sha256,
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
@runtime_checkable
|
|
2155
|
+
class CampaignSelectorRequestPromptRenderer(Protocol):
|
|
2156
|
+
"""Render the exact prompt sent by an opt-in campaign selector.
|
|
2157
|
+
|
|
2158
|
+
The default campaign path keeps using the stable direct-selector renderer.
|
|
2159
|
+
Selectors with a different wire contract must inject an implementation
|
|
2160
|
+
backed by the same authenticated pre-call inputs used for provider
|
|
2161
|
+
execution; reconstructing a plausible prompt after the call is not audit
|
|
2162
|
+
evidence.
|
|
2163
|
+
"""
|
|
2164
|
+
|
|
2165
|
+
def render(self, request: PortfolioSelectionRequest) -> str: ...
|
|
2166
|
+
|
|
2167
|
+
|
|
2168
|
+
@dataclass(frozen=True, slots=True)
|
|
2169
|
+
class CampaignIdentifiableReflectionEvidenceQuery:
|
|
2170
|
+
"""Content-minimized request for one committed mutation-evidence cohort.
|
|
2171
|
+
|
|
2172
|
+
The query deliberately carries the source *receipt identity*, never the
|
|
2173
|
+
source-stage payload or recombination results. Its sealed cutoff is the
|
|
2174
|
+
portfolio generation that produced the direct mutations; the later
|
|
2175
|
+
recombination generation only supplies the scheduling boundary.
|
|
2176
|
+
"""
|
|
2177
|
+
|
|
2178
|
+
reflection_request_sha256: str
|
|
2179
|
+
preparation_sha256: str
|
|
2180
|
+
runtime_start_receipt_sha256: str
|
|
2181
|
+
campaign_sha256: str
|
|
2182
|
+
workload_instance_sha256: str
|
|
2183
|
+
evaluator_contract_sha256: str
|
|
2184
|
+
wave: CampaignReflectionWave
|
|
2185
|
+
source_stage_receipt_sha256: str
|
|
2186
|
+
source_portfolio_generation: int
|
|
2187
|
+
prior_cutoff_event_index_exclusive: int
|
|
2188
|
+
sealed_cutoff_event_index_inclusive: int
|
|
2189
|
+
query_sha256: str = field(init=False)
|
|
2190
|
+
|
|
2191
|
+
def __post_init__(self) -> None:
|
|
2192
|
+
for name in (
|
|
2193
|
+
"reflection_request_sha256",
|
|
2194
|
+
"preparation_sha256",
|
|
2195
|
+
"runtime_start_receipt_sha256",
|
|
2196
|
+
"campaign_sha256",
|
|
2197
|
+
"workload_instance_sha256",
|
|
2198
|
+
"evaluator_contract_sha256",
|
|
2199
|
+
"source_stage_receipt_sha256",
|
|
2200
|
+
):
|
|
2201
|
+
_require_sha256(getattr(self, name), name)
|
|
2202
|
+
if type(self.wave) is not CampaignReflectionWave:
|
|
2203
|
+
raise TypeError("wave must be an exact CampaignReflectionWave")
|
|
2204
|
+
CampaignReflectionWave.__post_init__(self.wave)
|
|
2205
|
+
if (
|
|
2206
|
+
type(self.source_portfolio_generation) is not int
|
|
2207
|
+
or self.source_portfolio_generation <= 0
|
|
2208
|
+
or self.source_portfolio_generation >= self.wave.source_generation
|
|
2209
|
+
):
|
|
2210
|
+
raise ValueError(
|
|
2211
|
+
"source portfolio generation must precede the reflection source"
|
|
2212
|
+
)
|
|
2213
|
+
for name in (
|
|
2214
|
+
"prior_cutoff_event_index_exclusive",
|
|
2215
|
+
"sealed_cutoff_event_index_inclusive",
|
|
2216
|
+
):
|
|
2217
|
+
value = getattr(self, name)
|
|
2218
|
+
if type(value) is not int or value < 0:
|
|
2219
|
+
raise ValueError(f"{name} must be a non-negative exact integer")
|
|
2220
|
+
if self.sealed_cutoff_event_index_inclusive != self.source_portfolio_generation:
|
|
2221
|
+
raise ValueError(
|
|
2222
|
+
"sealed evidence cutoff must equal the source portfolio generation"
|
|
2223
|
+
)
|
|
2224
|
+
if (
|
|
2225
|
+
self.prior_cutoff_event_index_exclusive
|
|
2226
|
+
>= self.sealed_cutoff_event_index_inclusive
|
|
2227
|
+
):
|
|
2228
|
+
raise ValueError("reflection evidence cutoff must strictly advance")
|
|
2229
|
+
object.__setattr__(
|
|
2230
|
+
self,
|
|
2231
|
+
"query_sha256",
|
|
2232
|
+
_preparation_sha256(
|
|
2233
|
+
_IDENTIFIABLE_REFLECTION_QUERY_DOMAIN,
|
|
2234
|
+
_object(self._unsigned_record()),
|
|
2235
|
+
),
|
|
2236
|
+
)
|
|
2237
|
+
|
|
2238
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
2239
|
+
return {
|
|
2240
|
+
"schema_version": 1,
|
|
2241
|
+
"reflection_request_sha256": self.reflection_request_sha256,
|
|
2242
|
+
"preparation_sha256": self.preparation_sha256,
|
|
2243
|
+
"runtime_start_receipt_sha256": self.runtime_start_receipt_sha256,
|
|
2244
|
+
"campaign_sha256": self.campaign_sha256,
|
|
2245
|
+
"workload_instance_sha256": self.workload_instance_sha256,
|
|
2246
|
+
"evaluator_contract_sha256": self.evaluator_contract_sha256,
|
|
2247
|
+
"wave": self.wave.to_record(),
|
|
2248
|
+
"source_stage_receipt_sha256": self.source_stage_receipt_sha256,
|
|
2249
|
+
"source_portfolio_generation": self.source_portfolio_generation,
|
|
2250
|
+
"prior_cutoff_event_index_exclusive": (
|
|
2251
|
+
self.prior_cutoff_event_index_exclusive
|
|
2252
|
+
),
|
|
2253
|
+
"sealed_cutoff_event_index_inclusive": (
|
|
2254
|
+
self.sealed_cutoff_event_index_inclusive
|
|
2255
|
+
),
|
|
2256
|
+
"source_stage_payload_exposed": False,
|
|
2257
|
+
"recombination_results_exposed": False,
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
def to_record(self) -> dict[str, object]:
|
|
2261
|
+
self.__post_init__()
|
|
2262
|
+
return {**self._unsigned_record(), "query_sha256": self.query_sha256}
|
|
2263
|
+
|
|
2264
|
+
|
|
2265
|
+
@dataclass(frozen=True, slots=True)
|
|
2266
|
+
class CampaignIdentifiableReflectionEvidenceProjection:
|
|
2267
|
+
"""Evidence-source response bound to one exact committed registry cutoff."""
|
|
2268
|
+
|
|
2269
|
+
query_sha256: str
|
|
2270
|
+
registry_snapshot_sha256: str
|
|
2271
|
+
registry_captured_through_event_index: int
|
|
2272
|
+
evidence: IdentifiableReflectionEvidenceSnapshot
|
|
2273
|
+
projection_sha256: str = field(init=False)
|
|
2274
|
+
|
|
2275
|
+
def __post_init__(self) -> None:
|
|
2276
|
+
_require_sha256(self.query_sha256, "query_sha256")
|
|
2277
|
+
_require_sha256(self.registry_snapshot_sha256, "registry_snapshot_sha256")
|
|
2278
|
+
if (
|
|
2279
|
+
type(self.registry_captured_through_event_index) is not int
|
|
2280
|
+
or self.registry_captured_through_event_index <= 0
|
|
2281
|
+
):
|
|
2282
|
+
raise ValueError("registry_captured_through_event_index must be positive")
|
|
2283
|
+
if type(self.evidence) is not IdentifiableReflectionEvidenceSnapshot:
|
|
2284
|
+
raise TypeError(
|
|
2285
|
+
"evidence must be an exact IdentifiableReflectionEvidenceSnapshot"
|
|
2286
|
+
)
|
|
2287
|
+
IdentifiableReflectionEvidenceSnapshot.__post_init__(self.evidence)
|
|
2288
|
+
if (
|
|
2289
|
+
self.evidence.sealed_cutoff_event_index_inclusive
|
|
2290
|
+
!= self.registry_captured_through_event_index
|
|
2291
|
+
):
|
|
2292
|
+
raise ValueError("evidence cutoff differs from the registry snapshot")
|
|
2293
|
+
object.__setattr__(
|
|
2294
|
+
self,
|
|
2295
|
+
"projection_sha256",
|
|
2296
|
+
_preparation_sha256(
|
|
2297
|
+
_IDENTIFIABLE_REFLECTION_SOURCE_DOMAIN,
|
|
2298
|
+
_object(self._unsigned_record()),
|
|
2299
|
+
),
|
|
2300
|
+
)
|
|
2301
|
+
|
|
2302
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
2303
|
+
return {
|
|
2304
|
+
"schema_version": 1,
|
|
2305
|
+
"query_sha256": self.query_sha256,
|
|
2306
|
+
"registry_snapshot_sha256": self.registry_snapshot_sha256,
|
|
2307
|
+
"registry_captured_through_event_index": (
|
|
2308
|
+
self.registry_captured_through_event_index
|
|
2309
|
+
),
|
|
2310
|
+
"evidence_snapshot_sha256": self.evidence.snapshot_sha256,
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
def to_record(self) -> dict[str, object]:
|
|
2314
|
+
self.__post_init__()
|
|
2315
|
+
return {
|
|
2316
|
+
**self._unsigned_record(),
|
|
2317
|
+
"evidence": self.evidence.to_record(),
|
|
2318
|
+
"projection_sha256": self.projection_sha256,
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
|
|
2322
|
+
@dataclass(frozen=True, slots=True)
|
|
2323
|
+
class CampaignIdentifiableReflectionInput:
|
|
2324
|
+
"""Only information an identifiable production reflection may consume."""
|
|
2325
|
+
|
|
2326
|
+
query: CampaignIdentifiableReflectionEvidenceQuery
|
|
2327
|
+
source: CampaignIdentifiableReflectionEvidenceProjection
|
|
2328
|
+
input_sha256: str = field(init=False)
|
|
2329
|
+
|
|
2330
|
+
def __post_init__(self) -> None:
|
|
2331
|
+
if type(self.query) is not CampaignIdentifiableReflectionEvidenceQuery:
|
|
2332
|
+
raise TypeError("query must be exact")
|
|
2333
|
+
CampaignIdentifiableReflectionEvidenceQuery.__post_init__(self.query)
|
|
2334
|
+
if type(self.source) is not CampaignIdentifiableReflectionEvidenceProjection:
|
|
2335
|
+
raise TypeError("source must be exact")
|
|
2336
|
+
CampaignIdentifiableReflectionEvidenceProjection.__post_init__(self.source)
|
|
2337
|
+
evidence = self.source.evidence
|
|
2338
|
+
if self.source.query_sha256 != self.query.query_sha256:
|
|
2339
|
+
raise ValueError("evidence source belongs to a foreign reflection query")
|
|
2340
|
+
if (
|
|
2341
|
+
evidence.prior_cutoff_event_index_exclusive
|
|
2342
|
+
!= self.query.prior_cutoff_event_index_exclusive
|
|
2343
|
+
or evidence.sealed_cutoff_event_index_inclusive
|
|
2344
|
+
!= self.query.sealed_cutoff_event_index_inclusive
|
|
2345
|
+
):
|
|
2346
|
+
raise ValueError("evidence snapshot escapes the requested cutoffs")
|
|
2347
|
+
if (
|
|
2348
|
+
evidence.campaign_sha256 != self.query.campaign_sha256
|
|
2349
|
+
or evidence.workload_instance_sha256 != self.query.workload_instance_sha256
|
|
2350
|
+
or evidence.evaluator_contract_sha256
|
|
2351
|
+
!= self.query.evaluator_contract_sha256
|
|
2352
|
+
):
|
|
2353
|
+
raise ValueError("evidence snapshot escapes the requested scope")
|
|
2354
|
+
object.__setattr__(
|
|
2355
|
+
self,
|
|
2356
|
+
"input_sha256",
|
|
2357
|
+
_preparation_sha256(
|
|
2358
|
+
_IDENTIFIABLE_REFLECTION_INPUT_DOMAIN,
|
|
2359
|
+
_object(self._unsigned_record()),
|
|
2360
|
+
),
|
|
2361
|
+
)
|
|
2362
|
+
|
|
2363
|
+
@property
|
|
2364
|
+
def evidence(self) -> IdentifiableReflectionEvidenceSnapshot:
|
|
2365
|
+
return self.source.evidence
|
|
2366
|
+
|
|
2367
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
2368
|
+
return {
|
|
2369
|
+
"schema_version": 1,
|
|
2370
|
+
"query_sha256": self.query.query_sha256,
|
|
2371
|
+
"source_projection_sha256": self.source.projection_sha256,
|
|
2372
|
+
"evidence_snapshot_sha256": self.source.evidence.snapshot_sha256,
|
|
2373
|
+
"source_stage_payload_exposed": False,
|
|
2374
|
+
"recombination_results_exposed": False,
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
def to_record(self) -> dict[str, object]:
|
|
2378
|
+
self.__post_init__()
|
|
2379
|
+
return {
|
|
2380
|
+
**self._unsigned_record(),
|
|
2381
|
+
"query": self.query.to_record(),
|
|
2382
|
+
"source": self.source.to_record(),
|
|
2383
|
+
"input_sha256": self.input_sha256,
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
|
|
2387
|
+
@runtime_checkable
|
|
2388
|
+
class CampaignIdentifiableReflectionEvidenceSource(Protocol):
|
|
2389
|
+
"""Project a committed registry cohort without receiving stage payloads."""
|
|
2390
|
+
|
|
2391
|
+
campaign_sha256: str
|
|
2392
|
+
workload_instance_sha256: str
|
|
2393
|
+
evaluator_contract_sha256: str
|
|
2394
|
+
|
|
2395
|
+
def project(
|
|
2396
|
+
self,
|
|
2397
|
+
query: CampaignIdentifiableReflectionEvidenceQuery,
|
|
2398
|
+
) -> CampaignIdentifiableReflectionEvidenceProjection: ...
|
|
2399
|
+
|
|
2400
|
+
|
|
2401
|
+
@runtime_checkable
|
|
2402
|
+
class CampaignReflectionFalsificationSource(Protocol):
|
|
2403
|
+
"""Return feedback committed and visible at one reflection cutoff."""
|
|
2404
|
+
|
|
2405
|
+
def available(
|
|
2406
|
+
self,
|
|
2407
|
+
query: CampaignIdentifiableReflectionEvidenceQuery,
|
|
2408
|
+
) -> tuple[ReflectionFalsificationFeedback, ...]: ...
|
|
2409
|
+
|
|
2410
|
+
|
|
2411
|
+
@dataclass(frozen=True, slots=True)
|
|
2412
|
+
class CommittedRegistryIdentifiableReflectionEvidenceSource:
|
|
2413
|
+
"""Default adapter over an append-only committed campaign registry."""
|
|
2414
|
+
|
|
2415
|
+
registry: CampaignEvidenceRegistry
|
|
2416
|
+
campaign_sha256: str
|
|
2417
|
+
workload_instance_sha256: str
|
|
2418
|
+
evaluator_contract_sha256: str
|
|
2419
|
+
prior_falsifications: tuple[ReflectionFalsificationFeedback, ...] = ()
|
|
2420
|
+
falsification_source: CampaignReflectionFalsificationSource | None = None
|
|
2421
|
+
|
|
2422
|
+
def __post_init__(self) -> None:
|
|
2423
|
+
if type(self.registry) is not CampaignEvidenceRegistry:
|
|
2424
|
+
raise TypeError("registry must be an exact CampaignEvidenceRegistry")
|
|
2425
|
+
for name in (
|
|
2426
|
+
"campaign_sha256",
|
|
2427
|
+
"workload_instance_sha256",
|
|
2428
|
+
"evaluator_contract_sha256",
|
|
2429
|
+
):
|
|
2430
|
+
_require_sha256(getattr(self, name), name)
|
|
2431
|
+
if type(self.prior_falsifications) is not tuple or any(
|
|
2432
|
+
type(value) is not ReflectionFalsificationFeedback
|
|
2433
|
+
for value in self.prior_falsifications
|
|
2434
|
+
):
|
|
2435
|
+
raise TypeError("prior_falsifications must contain exact feedback")
|
|
2436
|
+
for value in self.prior_falsifications:
|
|
2437
|
+
ReflectionFalsificationFeedback.__post_init__(value)
|
|
2438
|
+
if self.falsification_source is not None and not isinstance(
|
|
2439
|
+
self.falsification_source,
|
|
2440
|
+
CampaignReflectionFalsificationSource,
|
|
2441
|
+
):
|
|
2442
|
+
raise TypeError(
|
|
2443
|
+
"falsification_source must implement its narrow runtime port"
|
|
2444
|
+
)
|
|
2445
|
+
if self.prior_falsifications and self.falsification_source is not None:
|
|
2446
|
+
raise ValueError(
|
|
2447
|
+
"static and cutoff-aware falsification sources are mutually exclusive"
|
|
2448
|
+
)
|
|
2449
|
+
|
|
2450
|
+
def project(
|
|
2451
|
+
self,
|
|
2452
|
+
query: CampaignIdentifiableReflectionEvidenceQuery,
|
|
2453
|
+
) -> CampaignIdentifiableReflectionEvidenceProjection:
|
|
2454
|
+
self.__post_init__()
|
|
2455
|
+
if type(query) is not CampaignIdentifiableReflectionEvidenceQuery:
|
|
2456
|
+
raise TypeError("query must be exact")
|
|
2457
|
+
CampaignIdentifiableReflectionEvidenceQuery.__post_init__(query)
|
|
2458
|
+
if (
|
|
2459
|
+
query.campaign_sha256 != self.campaign_sha256
|
|
2460
|
+
or query.workload_instance_sha256 != self.workload_instance_sha256
|
|
2461
|
+
or query.evaluator_contract_sha256 != self.evaluator_contract_sha256
|
|
2462
|
+
):
|
|
2463
|
+
raise ValueError("reflection query names a foreign evidence scope")
|
|
2464
|
+
cutoff = query.sealed_cutoff_event_index_inclusive
|
|
2465
|
+
if self.registry.captured_through_event_index < cutoff:
|
|
2466
|
+
raise RuntimeError("committed evidence registry has not reached the cutoff")
|
|
2467
|
+
committed = tuple(
|
|
2468
|
+
value for value in self.registry.observations if value.event_index <= cutoff
|
|
2469
|
+
)
|
|
2470
|
+
registry_snapshot = GlobalEvidenceRegistrySnapshot.seal(
|
|
2471
|
+
captured_through_event_index=cutoff,
|
|
2472
|
+
observations=committed,
|
|
2473
|
+
)
|
|
2474
|
+
dynamic_source = self.falsification_source
|
|
2475
|
+
if dynamic_source is None:
|
|
2476
|
+
candidates = self.prior_falsifications
|
|
2477
|
+
else:
|
|
2478
|
+
candidates = dynamic_source.available(query)
|
|
2479
|
+
if type(candidates) is not tuple or any(
|
|
2480
|
+
type(value) is not ReflectionFalsificationFeedback
|
|
2481
|
+
for value in candidates
|
|
2482
|
+
):
|
|
2483
|
+
raise TypeError(
|
|
2484
|
+
"falsification source must return exact feedback values"
|
|
2485
|
+
)
|
|
2486
|
+
for value in candidates:
|
|
2487
|
+
ReflectionFalsificationFeedback.__post_init__(value)
|
|
2488
|
+
if tuple(value.feedback_sha256 for value in candidates) != tuple(
|
|
2489
|
+
sorted({value.feedback_sha256 for value in candidates})
|
|
2490
|
+
):
|
|
2491
|
+
raise ValueError(
|
|
2492
|
+
"falsification source must return canonical unique feedback"
|
|
2493
|
+
)
|
|
2494
|
+
applicable_falsifications = tuple(
|
|
2495
|
+
value
|
|
2496
|
+
for value in candidates
|
|
2497
|
+
if value.available_event_index <= cutoff
|
|
2498
|
+
and self.workload_instance_sha256
|
|
2499
|
+
in value.applicable_workload_instance_sha256s
|
|
2500
|
+
and value.evaluator_contract_sha256 == self.evaluator_contract_sha256
|
|
2501
|
+
and (
|
|
2502
|
+
not value.applicable_campaign_sha256s
|
|
2503
|
+
or self.campaign_sha256 in value.applicable_campaign_sha256s
|
|
2504
|
+
)
|
|
2505
|
+
)
|
|
2506
|
+
if dynamic_source is not None and applicable_falsifications != candidates:
|
|
2507
|
+
raise ValueError(
|
|
2508
|
+
"cutoff-aware falsification source returned foreign or future feedback"
|
|
2509
|
+
)
|
|
2510
|
+
evidence = project_identifiable_reflection_evidence(
|
|
2511
|
+
registry_snapshot.observations,
|
|
2512
|
+
campaign_sha256=self.campaign_sha256,
|
|
2513
|
+
workload_instance_sha256=self.workload_instance_sha256,
|
|
2514
|
+
evaluator_contract_sha256=self.evaluator_contract_sha256,
|
|
2515
|
+
prior_cutoff_event_index_exclusive=(
|
|
2516
|
+
query.prior_cutoff_event_index_exclusive
|
|
2517
|
+
),
|
|
2518
|
+
sealed_cutoff_event_index_inclusive=cutoff,
|
|
2519
|
+
prior_falsifications=applicable_falsifications,
|
|
2520
|
+
)
|
|
2521
|
+
return CampaignIdentifiableReflectionEvidenceProjection(
|
|
2522
|
+
query_sha256=query.query_sha256,
|
|
2523
|
+
registry_snapshot_sha256=registry_snapshot.snapshot_sha256,
|
|
2524
|
+
registry_captured_through_event_index=cutoff,
|
|
2525
|
+
evidence=evidence,
|
|
2526
|
+
)
|
|
2527
|
+
|
|
2528
|
+
|
|
2529
|
+
@runtime_checkable
|
|
2530
|
+
class CampaignReflectionExecutor(Protocol):
|
|
2531
|
+
"""Execute reflection from sealed direct-mutation evidence only."""
|
|
2532
|
+
|
|
2533
|
+
async def reflect(
|
|
2534
|
+
self,
|
|
2535
|
+
reflection_input: CampaignIdentifiableReflectionInput,
|
|
2536
|
+
) -> FrozenJsonObject: ...
|
|
2537
|
+
|
|
2538
|
+
|
|
2539
|
+
@runtime_checkable
|
|
2540
|
+
class CampaignLegacyRecombinationReflectionExecutor(Protocol):
|
|
2541
|
+
"""Deprecated compatibility port exposing non-identifiable recombinations."""
|
|
2542
|
+
|
|
2543
|
+
async def reflect(
|
|
2544
|
+
self,
|
|
2545
|
+
request: CampaignReflectionRequest,
|
|
2546
|
+
source_results: tuple[PortfolioRecombinationWaveResult, ...],
|
|
2547
|
+
) -> FrozenJsonObject: ...
|
|
2548
|
+
|
|
2549
|
+
|
|
2550
|
+
@runtime_checkable
|
|
2551
|
+
class CampaignPortfolioOutcomeUpdater(Protocol):
|
|
2552
|
+
"""Prepare then atomically publish workload-visible outcome state.
|
|
2553
|
+
|
|
2554
|
+
Exact causal credit inside :class:`InsightMemoryBank` remains owned by the
|
|
2555
|
+
wave's ``PortfolioMemoryCreditPlan``. ``prepare_update`` must not publish
|
|
2556
|
+
any ledger or workload state. ``commit_update`` is a synchronous, no-I/O,
|
|
2557
|
+
already-validated publication and must not fail for its own preparation.
|
|
2558
|
+
"""
|
|
2559
|
+
|
|
2560
|
+
async def prepare_update(
|
|
2561
|
+
self,
|
|
2562
|
+
request: CampaignStageRequest,
|
|
2563
|
+
waves: tuple[PortfolioVariationWaveRequest, ...],
|
|
2564
|
+
results: tuple[PortfolioVariationWaveResult, ...],
|
|
2565
|
+
prior_memory: FrozenJsonObject,
|
|
2566
|
+
) -> CampaignPortfolioOutcomePreparation: ...
|
|
2567
|
+
|
|
2568
|
+
def commit_update(
|
|
2569
|
+
self,
|
|
2570
|
+
preparation: CampaignPortfolioOutcomePreparation,
|
|
2571
|
+
) -> None: ...
|
|
2572
|
+
|
|
2573
|
+
def abort_update(
|
|
2574
|
+
self,
|
|
2575
|
+
preparation: CampaignPortfolioOutcomePreparation,
|
|
2576
|
+
) -> None: ...
|
|
2577
|
+
|
|
2578
|
+
|
|
2579
|
+
@runtime_checkable
|
|
2580
|
+
class CampaignLearningLifecyclePort(Protocol):
|
|
2581
|
+
"""Join quarantined reflections to later diagnostic campaign barriers.
|
|
2582
|
+
|
|
2583
|
+
This is an observation/lifecycle seam, not a workload or provider API. It
|
|
2584
|
+
may stage exact reflection cards, admit them for controlled testing, and
|
|
2585
|
+
adjudicate them after a complete memory-credit barrier. Every method
|
|
2586
|
+
returns frozen evidence for the campaign trace.
|
|
2587
|
+
"""
|
|
2588
|
+
|
|
2589
|
+
def reflection_completed(
|
|
2590
|
+
self,
|
|
2591
|
+
request: CampaignReflectionRequest,
|
|
2592
|
+
receipt: CampaignReflectionReceipt,
|
|
2593
|
+
result: FrozenJsonObject,
|
|
2594
|
+
) -> FrozenJsonObject: ...
|
|
2595
|
+
|
|
2596
|
+
def reflections_admitted(
|
|
2597
|
+
self,
|
|
2598
|
+
request: CampaignReflectionTestAdmissionRequest,
|
|
2599
|
+
contents: tuple[tuple[CampaignReflectionReceipt, FrozenJsonObject], ...],
|
|
2600
|
+
) -> FrozenJsonObject: ...
|
|
2601
|
+
|
|
2602
|
+
async def prepare_portfolio_generation_close(
|
|
2603
|
+
self,
|
|
2604
|
+
request: CampaignStageRequest,
|
|
2605
|
+
waves: tuple[PortfolioVariationWaveRequest, ...],
|
|
2606
|
+
results: tuple[PortfolioVariationWaveResult, ...],
|
|
2607
|
+
memory_credit_preparation: PortfolioMemoryCreditBatchPreparation,
|
|
2608
|
+
) -> CampaignPortfolioLearningPreparation: ...
|
|
2609
|
+
|
|
2610
|
+
def commit_portfolio_generation_close(
|
|
2611
|
+
self,
|
|
2612
|
+
preparation: CampaignPortfolioLearningPreparation,
|
|
2613
|
+
) -> None: ...
|
|
2614
|
+
|
|
2615
|
+
def abort_portfolio_generation_close(
|
|
2616
|
+
self,
|
|
2617
|
+
preparation: CampaignPortfolioLearningPreparation,
|
|
2618
|
+
) -> None: ...
|
|
2619
|
+
|
|
2620
|
+
|
|
2621
|
+
@runtime_checkable
|
|
2622
|
+
class CampaignRecombinationUtilityBinder(Protocol):
|
|
2623
|
+
"""Bind exact scored-source/pair utility under the generation cutoff.
|
|
2624
|
+
|
|
2625
|
+
``source_result.candidates`` is the ranked ITT population;
|
|
2626
|
+
``source_result.scored_candidates`` is the only utility/parent universe.
|
|
2627
|
+
"""
|
|
2628
|
+
|
|
2629
|
+
def bind(
|
|
2630
|
+
self,
|
|
2631
|
+
*,
|
|
2632
|
+
source_archive_utility: ArchiveUtilitySnapshot,
|
|
2633
|
+
source_wave: PortfolioVariationWaveRequest,
|
|
2634
|
+
source_result: PortfolioVariationWaveResult,
|
|
2635
|
+
) -> FrozenArchiveSourceUtilityReceipt: ...
|
|
2636
|
+
|
|
2637
|
+
|
|
2638
|
+
@runtime_checkable
|
|
2639
|
+
class CampaignOwnedRuntimeResourcePort(Protocol):
|
|
2640
|
+
"""Close a provider queue or other resource explicitly owned by the adapter."""
|
|
2641
|
+
|
|
2642
|
+
async def close(self) -> FrozenJsonObject: ...
|
|
2643
|
+
|
|
2644
|
+
|
|
2645
|
+
@dataclass(slots=True)
|
|
2646
|
+
class AgenticPortfolioCampaignRuntime:
|
|
2647
|
+
"""Run a prepared campaign through the real AgentEvolve application stack."""
|
|
2648
|
+
|
|
2649
|
+
prepared: PreparedEvolutionCampaign
|
|
2650
|
+
workload_config: AgenticCampaignWorkloadConfig
|
|
2651
|
+
workload_ports: CampaignWorkloadPorts
|
|
2652
|
+
composition: PortfolioEvolutionComposition
|
|
2653
|
+
parent_selector: CampaignParentSelectionPort
|
|
2654
|
+
wave_factory: CampaignPortfolioWaveFactory
|
|
2655
|
+
task_sha256: str
|
|
2656
|
+
parent_measurement_projection: ParentMeasurementProjection | None = None
|
|
2657
|
+
archive_context_projector: CampaignPortfolioArchiveContextProjector | None = None
|
|
2658
|
+
context_enricher: CampaignPortfolioContextEnricher | None = None
|
|
2659
|
+
contextual_search_planner: CampaignContextualSearchPlanner | None = None
|
|
2660
|
+
frontier_target_allocator: CampaignPortfolioFrontierTargetAllocator | None = None
|
|
2661
|
+
variation_envelope: CampaignVariationEnvelopePolicy | None = None
|
|
2662
|
+
memory_estimand_projector: CampaignPortfolioMemoryEstimandProjector | None = None
|
|
2663
|
+
learning_lifecycle: CampaignLearningLifecyclePort | None = None
|
|
2664
|
+
identifiable_reflection_executor: CampaignReflectionExecutor | None = None
|
|
2665
|
+
identifiable_reflection_evidence_source: (
|
|
2666
|
+
CampaignIdentifiableReflectionEvidenceSource | None
|
|
2667
|
+
) = None
|
|
2668
|
+
legacy_recombination_reflection_executor: (
|
|
2669
|
+
CampaignLegacyRecombinationReflectionExecutor | None
|
|
2670
|
+
) = None
|
|
2671
|
+
# Temporary source-compatible alias. It is always interpreted as the
|
|
2672
|
+
# legacy recombination port and cannot be combined with the identifiable
|
|
2673
|
+
# pair or the explicitly named legacy field.
|
|
2674
|
+
reflection_executor: CampaignLegacyRecombinationReflectionExecutor | None = None
|
|
2675
|
+
outcome_updater: CampaignPortfolioOutcomeUpdater | None = None
|
|
2676
|
+
recombination_utility_binder: CampaignRecombinationUtilityBinder | None = None
|
|
2677
|
+
owned_resources: CampaignOwnedRuntimeResourcePort | None = None
|
|
2678
|
+
recombination: PortfolioRecombination | None = None
|
|
2679
|
+
capacity_recourse: CampaignCapacityRecoursePort | None = None
|
|
2680
|
+
recombination_evaluation_allocation_mode: RecombinationEvaluationAllocationMode = (
|
|
2681
|
+
RecombinationEvaluationAllocationMode.NATIVE_THEN_RECOURSE
|
|
2682
|
+
)
|
|
2683
|
+
selector_request_prompt_renderer: CampaignSelectorRequestPromptRenderer | None = (
|
|
2684
|
+
None
|
|
2685
|
+
)
|
|
2686
|
+
wave_preparation_observer: CampaignPortfolioWavePreparationObserver | None = None
|
|
2687
|
+
archive: ParetoArchive = field(init=False)
|
|
2688
|
+
_history: list[EvolutionCandidate] = field(init=False, default_factory=list)
|
|
2689
|
+
_memory: FrozenJsonObject | None = field(init=False, default=None)
|
|
2690
|
+
_portfolio_waves: dict[
|
|
2691
|
+
int,
|
|
2692
|
+
tuple[tuple[PortfolioVariationWaveRequest, PortfolioVariationWaveResult], ...],
|
|
2693
|
+
] = field(init=False, default_factory=dict)
|
|
2694
|
+
_recombination_results: dict[int, tuple[PortfolioRecombinationWaveResult, ...]] = (
|
|
2695
|
+
field(init=False, default_factory=dict)
|
|
2696
|
+
)
|
|
2697
|
+
_recombination_source_portfolio_generations: dict[int, int] = field(
|
|
2698
|
+
init=False,
|
|
2699
|
+
default_factory=dict,
|
|
2700
|
+
)
|
|
2701
|
+
_stage_receipts: dict[int, CampaignStageReceipt] = field(
|
|
2702
|
+
init=False, default_factory=dict
|
|
2703
|
+
)
|
|
2704
|
+
_parent_selection_progress: list[CampaignParentSelectionProgress] = field(
|
|
2705
|
+
init=False,
|
|
2706
|
+
default_factory=list,
|
|
2707
|
+
)
|
|
2708
|
+
_pending_parent_selection_progress: (
|
|
2709
|
+
_PendingCampaignParentSelectionProgress | None
|
|
2710
|
+
) = field(init=False, default=None)
|
|
2711
|
+
_archive_utilities: dict[int, ArchiveUtilitySnapshot] = field(
|
|
2712
|
+
init=False, default_factory=dict
|
|
2713
|
+
)
|
|
2714
|
+
_reflection_results: dict[str, FrozenJsonObject] = field(
|
|
2715
|
+
init=False, default_factory=dict
|
|
2716
|
+
)
|
|
2717
|
+
_reflection_learning_evidence: dict[str, FrozenJsonObject] = field(
|
|
2718
|
+
init=False,
|
|
2719
|
+
default_factory=dict,
|
|
2720
|
+
)
|
|
2721
|
+
_identifiable_reflection_inputs: dict[
|
|
2722
|
+
str,
|
|
2723
|
+
CampaignIdentifiableReflectionInput,
|
|
2724
|
+
] = field(init=False, default_factory=dict)
|
|
2725
|
+
_identifiable_reflection_abstentions: dict[str, FrozenJsonObject] = field(
|
|
2726
|
+
init=False,
|
|
2727
|
+
default_factory=dict,
|
|
2728
|
+
)
|
|
2729
|
+
_consumed_reflection_source_evidence_ids: set[str] = field(
|
|
2730
|
+
init=False,
|
|
2731
|
+
default_factory=set,
|
|
2732
|
+
)
|
|
2733
|
+
_last_identifiable_reflection_cutoff: int = field(init=False, default=0)
|
|
2734
|
+
_test_eligible_reflection_hashes: set[str] = field(init=False, default_factory=set)
|
|
2735
|
+
_started: bool = field(init=False, default=False)
|
|
2736
|
+
_cleaned: bool = field(init=False, default=False)
|
|
2737
|
+
_selector_calls: int = field(init=False, default=0)
|
|
2738
|
+
_wave_preparation_receipts: list[CampaignPortfolioWavePreparationReceipt] = field(
|
|
2739
|
+
init=False, default_factory=list
|
|
2740
|
+
)
|
|
2741
|
+
_phenotype_identity_cache: dict[str, str] = field(
|
|
2742
|
+
init=False,
|
|
2743
|
+
default_factory=dict,
|
|
2744
|
+
)
|
|
2745
|
+
|
|
2746
|
+
def __post_init__(self) -> None:
|
|
2747
|
+
if type(self.prepared) is not PreparedEvolutionCampaign:
|
|
2748
|
+
raise TypeError("prepared must be an exact PreparedEvolutionCampaign")
|
|
2749
|
+
PreparedEvolutionCampaign.__post_init__(self.prepared)
|
|
2750
|
+
if type(self.workload_config) is not AgenticCampaignWorkloadConfig:
|
|
2751
|
+
raise TypeError("workload_config must be exact")
|
|
2752
|
+
AgenticCampaignWorkloadConfig.__post_init__(self.workload_config)
|
|
2753
|
+
if type(self.composition) is not PortfolioEvolutionComposition:
|
|
2754
|
+
raise TypeError("composition must be exact")
|
|
2755
|
+
PortfolioEvolutionComposition.__post_init__(self.composition)
|
|
2756
|
+
if self.composition.benchmark is not self.workload_config.benchmark:
|
|
2757
|
+
raise ValueError("composition is bound to a different benchmark object")
|
|
2758
|
+
if not isinstance(self.parent_selector, CampaignParentSelectionPort):
|
|
2759
|
+
raise TypeError(
|
|
2760
|
+
"parent_selector must implement CampaignParentSelectionPort"
|
|
2761
|
+
)
|
|
2762
|
+
if not isinstance(self.wave_factory, CampaignPortfolioWaveFactory):
|
|
2763
|
+
raise TypeError("wave_factory must implement CampaignPortfolioWaveFactory")
|
|
2764
|
+
if self.archive_context_projector is not None and not isinstance(
|
|
2765
|
+
self.archive_context_projector,
|
|
2766
|
+
CampaignPortfolioArchiveContextProjector,
|
|
2767
|
+
):
|
|
2768
|
+
raise TypeError("archive_context_projector must implement its runtime port")
|
|
2769
|
+
if self.context_enricher is not None and not isinstance(
|
|
2770
|
+
self.context_enricher,
|
|
2771
|
+
CampaignPortfolioContextEnricher,
|
|
2772
|
+
):
|
|
2773
|
+
raise TypeError("context_enricher must implement its runtime port")
|
|
2774
|
+
if (
|
|
2775
|
+
self.contextual_search_planner is not None
|
|
2776
|
+
and type(self.contextual_search_planner)
|
|
2777
|
+
is not CampaignContextualSearchPlanner
|
|
2778
|
+
):
|
|
2779
|
+
raise TypeError("contextual_search_planner must be exact or None")
|
|
2780
|
+
if self.frontier_target_allocator is not None and not isinstance(
|
|
2781
|
+
self.frontier_target_allocator,
|
|
2782
|
+
CampaignPortfolioFrontierTargetAllocator,
|
|
2783
|
+
):
|
|
2784
|
+
raise TypeError("frontier_target_allocator must implement its runtime port")
|
|
2785
|
+
if (
|
|
2786
|
+
self.contextual_search_planner is not None
|
|
2787
|
+
and self.frontier_target_allocator is not None
|
|
2788
|
+
):
|
|
2789
|
+
raise ValueError(
|
|
2790
|
+
"contextual and standalone frontier target allocation are mutually "
|
|
2791
|
+
"exclusive"
|
|
2792
|
+
)
|
|
2793
|
+
if self.variation_envelope is not None and not isinstance(
|
|
2794
|
+
self.variation_envelope,
|
|
2795
|
+
CampaignVariationEnvelopePolicy,
|
|
2796
|
+
):
|
|
2797
|
+
raise TypeError(
|
|
2798
|
+
"variation_envelope must implement CampaignVariationEnvelopePolicy"
|
|
2799
|
+
)
|
|
2800
|
+
if self.memory_estimand_projector is not None and not isinstance(
|
|
2801
|
+
self.memory_estimand_projector,
|
|
2802
|
+
CampaignPortfolioMemoryEstimandProjector,
|
|
2803
|
+
):
|
|
2804
|
+
raise TypeError("memory_estimand_projector must implement its runtime port")
|
|
2805
|
+
if self.wave_preparation_observer is not None and not isinstance(
|
|
2806
|
+
self.wave_preparation_observer,
|
|
2807
|
+
CampaignPortfolioWavePreparationObserver,
|
|
2808
|
+
):
|
|
2809
|
+
raise TypeError("wave_preparation_observer must implement its runtime port")
|
|
2810
|
+
if self.learning_lifecycle is not None and not isinstance(
|
|
2811
|
+
self.learning_lifecycle,
|
|
2812
|
+
CampaignLearningLifecyclePort,
|
|
2813
|
+
):
|
|
2814
|
+
raise TypeError("learning_lifecycle must implement its runtime port")
|
|
2815
|
+
if self.selector_request_prompt_renderer is not None and not isinstance(
|
|
2816
|
+
self.selector_request_prompt_renderer,
|
|
2817
|
+
CampaignSelectorRequestPromptRenderer,
|
|
2818
|
+
):
|
|
2819
|
+
raise TypeError(
|
|
2820
|
+
"selector_request_prompt_renderer must implement its runtime port"
|
|
2821
|
+
)
|
|
2822
|
+
identifiable_executor = self.identifiable_reflection_executor
|
|
2823
|
+
evidence_source = self.identifiable_reflection_evidence_source
|
|
2824
|
+
explicitly_legacy = self.legacy_recombination_reflection_executor
|
|
2825
|
+
compatibility_legacy = self.reflection_executor
|
|
2826
|
+
if identifiable_executor is not None and not isinstance(
|
|
2827
|
+
identifiable_executor,
|
|
2828
|
+
CampaignReflectionExecutor,
|
|
2829
|
+
):
|
|
2830
|
+
raise TypeError(
|
|
2831
|
+
"identifiable_reflection_executor must implement its runtime port"
|
|
2832
|
+
)
|
|
2833
|
+
if evidence_source is not None and not isinstance(
|
|
2834
|
+
evidence_source,
|
|
2835
|
+
CampaignIdentifiableReflectionEvidenceSource,
|
|
2836
|
+
):
|
|
2837
|
+
raise TypeError(
|
|
2838
|
+
"identifiable_reflection_evidence_source must implement its runtime port"
|
|
2839
|
+
)
|
|
2840
|
+
if evidence_source is not None:
|
|
2841
|
+
for name in (
|
|
2842
|
+
"campaign_sha256",
|
|
2843
|
+
"workload_instance_sha256",
|
|
2844
|
+
"evaluator_contract_sha256",
|
|
2845
|
+
):
|
|
2846
|
+
_require_sha256(
|
|
2847
|
+
getattr(evidence_source, name),
|
|
2848
|
+
f"identifiable_reflection_evidence_source.{name}",
|
|
2849
|
+
)
|
|
2850
|
+
if (identifiable_executor is None) != (evidence_source is None):
|
|
2851
|
+
raise ValueError(
|
|
2852
|
+
"identifiable reflection requires both executor and evidence source"
|
|
2853
|
+
)
|
|
2854
|
+
if explicitly_legacy is not None and not isinstance(
|
|
2855
|
+
explicitly_legacy,
|
|
2856
|
+
CampaignLegacyRecombinationReflectionExecutor,
|
|
2857
|
+
):
|
|
2858
|
+
raise TypeError(
|
|
2859
|
+
"legacy_recombination_reflection_executor must implement its runtime port"
|
|
2860
|
+
)
|
|
2861
|
+
if compatibility_legacy is not None and not isinstance(
|
|
2862
|
+
compatibility_legacy,
|
|
2863
|
+
CampaignLegacyRecombinationReflectionExecutor,
|
|
2864
|
+
):
|
|
2865
|
+
raise TypeError(
|
|
2866
|
+
"reflection_executor compatibility alias must implement the legacy port"
|
|
2867
|
+
)
|
|
2868
|
+
if explicitly_legacy is not None and compatibility_legacy is not None:
|
|
2869
|
+
raise ValueError("legacy reflection executor was configured twice")
|
|
2870
|
+
if identifiable_executor is not None and (
|
|
2871
|
+
explicitly_legacy is not None or compatibility_legacy is not None
|
|
2872
|
+
):
|
|
2873
|
+
raise ValueError(
|
|
2874
|
+
"identifiable and legacy reflection modes are mutually exclusive"
|
|
2875
|
+
)
|
|
2876
|
+
if self.outcome_updater is not None and not isinstance(
|
|
2877
|
+
self.outcome_updater, CampaignPortfolioOutcomeUpdater
|
|
2878
|
+
):
|
|
2879
|
+
raise TypeError("outcome_updater must implement its runtime port")
|
|
2880
|
+
if self.recombination_utility_binder is not None and not isinstance(
|
|
2881
|
+
self.recombination_utility_binder,
|
|
2882
|
+
CampaignRecombinationUtilityBinder,
|
|
2883
|
+
):
|
|
2884
|
+
raise TypeError(
|
|
2885
|
+
"recombination_utility_binder must implement its runtime port"
|
|
2886
|
+
)
|
|
2887
|
+
if self.capacity_recourse is not None:
|
|
2888
|
+
validate_campaign_capacity_recourse_port(self.capacity_recourse)
|
|
2889
|
+
if type(self.recombination_evaluation_allocation_mode) is not (
|
|
2890
|
+
RecombinationEvaluationAllocationMode
|
|
2891
|
+
):
|
|
2892
|
+
raise TypeError(
|
|
2893
|
+
"recombination_evaluation_allocation_mode must be exact"
|
|
2894
|
+
)
|
|
2895
|
+
if (
|
|
2896
|
+
self.recombination_evaluation_allocation_mode
|
|
2897
|
+
is RecombinationEvaluationAllocationMode.RECOURSE_ONLY
|
|
2898
|
+
and self.capacity_recourse is None
|
|
2899
|
+
):
|
|
2900
|
+
raise ValueError("recourse-only allocation requires a recourse port")
|
|
2901
|
+
if self.owned_resources is not None and not isinstance(
|
|
2902
|
+
self.owned_resources, CampaignOwnedRuntimeResourcePort
|
|
2903
|
+
):
|
|
2904
|
+
raise TypeError("owned_resources must implement its runtime port")
|
|
2905
|
+
if (
|
|
2906
|
+
type(self.task_sha256) is not str
|
|
2907
|
+
or len(self.task_sha256) != 64
|
|
2908
|
+
or any(value not in "0123456789abcdef" for value in self.task_sha256)
|
|
2909
|
+
):
|
|
2910
|
+
raise ValueError("task_sha256 must be a lowercase SHA-256 digest")
|
|
2911
|
+
if type(self.workload_ports) is not CampaignWorkloadPorts:
|
|
2912
|
+
raise TypeError("workload_ports must be exact")
|
|
2913
|
+
CampaignWorkloadPorts.__post_init__(self.workload_ports)
|
|
2914
|
+
if self.workload_ports.ports_sha256 != self.prepared.workload_ports_sha256:
|
|
2915
|
+
raise ValueError("runtime workload ports differ from preparation")
|
|
2916
|
+
if (
|
|
2917
|
+
self.prepared.benchmark_session.benchmark
|
|
2918
|
+
!= self.workload_config.benchmark_record
|
|
2919
|
+
):
|
|
2920
|
+
raise ValueError("prepared session describes a different benchmark")
|
|
2921
|
+
measurement_projection = self.parent_measurement_projection
|
|
2922
|
+
if measurement_projection is not None:
|
|
2923
|
+
if type(measurement_projection) is not ParentMeasurementProjection:
|
|
2924
|
+
raise TypeError("parent_measurement_projection must be exact or None")
|
|
2925
|
+
measurement_projection.__post_init__()
|
|
2926
|
+
benchmark_sha256 = typed_json_sha256(
|
|
2927
|
+
self.prepared.benchmark_session.benchmark
|
|
2928
|
+
)
|
|
2929
|
+
if (
|
|
2930
|
+
measurement_projection.benchmark_sha256 != benchmark_sha256
|
|
2931
|
+
or measurement_projection.session_sha256
|
|
2932
|
+
!= self.prepared.benchmark_session.session_sha256
|
|
2933
|
+
):
|
|
2934
|
+
raise ValueError(
|
|
2935
|
+
"parent measurement projection names a foreign campaign"
|
|
2936
|
+
)
|
|
2937
|
+
benchmark = self.workload_config.benchmark
|
|
2938
|
+
evaluator = benchmark.detailed_evaluator
|
|
2939
|
+
if (
|
|
2940
|
+
evaluator is None
|
|
2941
|
+
or (
|
|
2942
|
+
evaluator.evaluator_identity.evaluator_id,
|
|
2943
|
+
evaluator.evaluator_identity.evaluator_version,
|
|
2944
|
+
evaluator.evaluator_identity.evaluator_context_sha256,
|
|
2945
|
+
)
|
|
2946
|
+
!= measurement_projection.evaluator_identity
|
|
2947
|
+
):
|
|
2948
|
+
raise ValueError(
|
|
2949
|
+
"parent measurement projection differs from benchmark evaluator"
|
|
2950
|
+
)
|
|
2951
|
+
expected_resolution = (
|
|
2952
|
+
(
|
|
2953
|
+
EXACT_OBJECTIVE_RESOLUTION_POLICY_ID,
|
|
2954
|
+
EXACT_OBJECTIVE_RESOLUTION_POLICY_VERSION,
|
|
2955
|
+
EXACT_OBJECTIVE_RESOLUTION_DEFINITION_SHA256,
|
|
2956
|
+
)
|
|
2957
|
+
if benchmark.objective_resolution is None
|
|
2958
|
+
else objective_resolution_policy_metadata(
|
|
2959
|
+
benchmark.objective_resolution
|
|
2960
|
+
)
|
|
2961
|
+
)
|
|
2962
|
+
if (
|
|
2963
|
+
measurement_projection.objective_resolution_identity
|
|
2964
|
+
!= expected_resolution
|
|
2965
|
+
):
|
|
2966
|
+
raise ValueError(
|
|
2967
|
+
"parent measurement projection differs from objective resolution"
|
|
2968
|
+
)
|
|
2969
|
+
semantics = benchmark.optimization_semantics
|
|
2970
|
+
if semantics is not None and (
|
|
2971
|
+
measurement_projection.decision_metrics.optimization_semantics_definition_sha256
|
|
2972
|
+
!= semantics.definition_sha256
|
|
2973
|
+
):
|
|
2974
|
+
raise ValueError(
|
|
2975
|
+
"parent measurement metric schema differs from benchmark semantics"
|
|
2976
|
+
)
|
|
2977
|
+
self.archive = ParetoArchive(
|
|
2978
|
+
self.composition.benchmark.objectives,
|
|
2979
|
+
outcome_relation_binding=self.composition.outcome_relation,
|
|
2980
|
+
)
|
|
2981
|
+
if self.recombination is None:
|
|
2982
|
+
self.recombination = PortfolioRecombination(
|
|
2983
|
+
engine=self.composition.engine,
|
|
2984
|
+
ids=self.composition.id_factory,
|
|
2985
|
+
selection_limit=(self.prepared.protocol.recombinations_per_parent),
|
|
2986
|
+
)
|
|
2987
|
+
elif type(self.recombination) is not PortfolioRecombination:
|
|
2988
|
+
raise TypeError("recombination must be exact or None")
|
|
2989
|
+
if (
|
|
2990
|
+
self.recombination.engine is not self.composition.engine
|
|
2991
|
+
or self.recombination.ids is not self.composition.id_factory
|
|
2992
|
+
or self.recombination.selection_limit
|
|
2993
|
+
!= self.prepared.protocol.recombinations_per_parent
|
|
2994
|
+
):
|
|
2995
|
+
raise ValueError(
|
|
2996
|
+
"recombination is bound to a different composition or protocol"
|
|
2997
|
+
)
|
|
2998
|
+
|
|
2999
|
+
@property
|
|
3000
|
+
def history(self) -> tuple[EvolutionCandidate, ...]:
|
|
3001
|
+
return tuple(self._history)
|
|
3002
|
+
|
|
3003
|
+
@property
|
|
3004
|
+
def final_front(self) -> tuple[EvolutionCandidate, ...]:
|
|
3005
|
+
return self.archive.front
|
|
3006
|
+
|
|
3007
|
+
@property
|
|
3008
|
+
def wave_preparation_receipts(
|
|
3009
|
+
self,
|
|
3010
|
+
) -> tuple[CampaignPortfolioWavePreparationReceipt, ...]:
|
|
3011
|
+
"""Validated prospective waves, including waves from failed stages."""
|
|
3012
|
+
|
|
3013
|
+
return tuple(self._wave_preparation_receipts)
|
|
3014
|
+
|
|
3015
|
+
async def start(
|
|
3016
|
+
self,
|
|
3017
|
+
prepared: PreparedEvolutionCampaign,
|
|
3018
|
+
) -> CampaignExecutionStartReceipt:
|
|
3019
|
+
if self._started:
|
|
3020
|
+
raise RuntimeError("campaign runtime is one-shot")
|
|
3021
|
+
if prepared.preparation_sha256 != self.prepared.preparation_sha256:
|
|
3022
|
+
raise ValueError("lifecycle start received a foreign preparation")
|
|
3023
|
+
cache = await self.composition.engine.evaluation_cache_snapshot()
|
|
3024
|
+
if _cache_misses(cache) != 0 or int(cache.get("hits") or 0) != 0:
|
|
3025
|
+
raise RuntimeError("campaign runtime requires a fresh evaluation cache")
|
|
3026
|
+
|
|
3027
|
+
candidates: list[EvolutionCandidate] = []
|
|
3028
|
+
receipts: list[CampaignSeedExecutionReceipt] = []
|
|
3029
|
+
for seed in prepared.seeds.seeds:
|
|
3030
|
+
before = _cache_misses(
|
|
3031
|
+
await self.composition.engine.evaluation_cache_snapshot()
|
|
3032
|
+
)
|
|
3033
|
+
configuration = thaw_json(seed.configuration)
|
|
3034
|
+
if type(configuration) is not dict:
|
|
3035
|
+
raise TypeError("campaign seed root must be an object")
|
|
3036
|
+
candidate = await self.composition.engine.register_seed(
|
|
3037
|
+
configuration,
|
|
3038
|
+
label=f"campaign_seed_{seed.seed_id}",
|
|
3039
|
+
)
|
|
3040
|
+
after = _cache_misses(
|
|
3041
|
+
await self.composition.engine.evaluation_cache_snapshot()
|
|
3042
|
+
)
|
|
3043
|
+
if after - before not in {0, 1}:
|
|
3044
|
+
raise RuntimeError("one seed produced invalid cache accounting")
|
|
3045
|
+
candidates.append(candidate)
|
|
3046
|
+
receipts.append(
|
|
3047
|
+
CampaignSeedExecutionReceipt(
|
|
3048
|
+
seed_id=seed.seed_id,
|
|
3049
|
+
configuration_sha256=seed.configuration_sha256,
|
|
3050
|
+
evaluated=True,
|
|
3051
|
+
unique_evaluation=after > before,
|
|
3052
|
+
valid=candidate.valid,
|
|
3053
|
+
failure_type=None if candidate.valid else "benchmark_invalid",
|
|
3054
|
+
evidence=_object({"candidate": _candidate_record(candidate)}),
|
|
3055
|
+
)
|
|
3056
|
+
)
|
|
3057
|
+
|
|
3058
|
+
self._memory = self.workload_ports.evidence.initialize_memory(
|
|
3059
|
+
self.prepared.benchmark_session,
|
|
3060
|
+
self.prepared.seeds,
|
|
3061
|
+
)
|
|
3062
|
+
for candidate in candidates:
|
|
3063
|
+
self._history.append(candidate)
|
|
3064
|
+
self.archive.consider(candidate)
|
|
3065
|
+
self._started = True
|
|
3066
|
+
return CampaignExecutionStartReceipt(
|
|
3067
|
+
preparation_sha256=prepared.preparation_sha256,
|
|
3068
|
+
runtime_preflight_receipt_sha256=(prepared.runtime_receipt.receipt_sha256),
|
|
3069
|
+
runtime_session_id=(f"campaign_runtime_{prepared.preparation_sha256[:24]}"),
|
|
3070
|
+
seed_batch_sha256=prepared.seeds.batch_sha256,
|
|
3071
|
+
seed_receipts=tuple(receipts),
|
|
3072
|
+
evidence=_object(
|
|
3073
|
+
{
|
|
3074
|
+
"runtime": "agentic_portfolio_campaign",
|
|
3075
|
+
"provider_calls_during_start": 0,
|
|
3076
|
+
"archive_snapshot_sha256": pareto_archive_snapshot_hash(
|
|
3077
|
+
self.archive.snapshot()
|
|
3078
|
+
),
|
|
3079
|
+
"memory_sha256": typed_json_sha256(self._memory),
|
|
3080
|
+
}
|
|
3081
|
+
),
|
|
3082
|
+
)
|
|
3083
|
+
|
|
3084
|
+
def _require_active(self) -> None:
|
|
3085
|
+
if not self._started or self._cleaned:
|
|
3086
|
+
raise RuntimeError("campaign runtime is not active")
|
|
3087
|
+
|
|
3088
|
+
def _archive_record(self) -> FrozenJsonObject:
|
|
3089
|
+
return self._archive_record_for(self.archive)
|
|
3090
|
+
|
|
3091
|
+
@staticmethod
|
|
3092
|
+
def _archive_record_for(archive: ParetoArchive) -> FrozenJsonObject:
|
|
3093
|
+
if type(archive) is not ParetoArchive:
|
|
3094
|
+
raise TypeError("archive must be an exact ParetoArchive")
|
|
3095
|
+
snapshot = archive.snapshot()
|
|
3096
|
+
return _object(
|
|
3097
|
+
{
|
|
3098
|
+
"snapshot_sha256": pareto_archive_snapshot_hash(snapshot),
|
|
3099
|
+
"summary": snapshot.to_trace_record(),
|
|
3100
|
+
"front_candidates": [
|
|
3101
|
+
_candidate_record(candidate)
|
|
3102
|
+
for candidate in snapshot.front_candidates
|
|
3103
|
+
],
|
|
3104
|
+
}
|
|
3105
|
+
)
|
|
3106
|
+
|
|
3107
|
+
def _preview_archive(
|
|
3108
|
+
self,
|
|
3109
|
+
candidates: tuple[EvolutionCandidate, ...],
|
|
3110
|
+
) -> ParetoArchive:
|
|
3111
|
+
"""Replay then extend the archive without changing live campaign state."""
|
|
3112
|
+
|
|
3113
|
+
current = self.archive.snapshot()
|
|
3114
|
+
preview = ParetoArchive(
|
|
3115
|
+
current.objectives,
|
|
3116
|
+
evidence_admission_policy=current.evidence_admission_policy,
|
|
3117
|
+
outcome_relation_binding=self.archive.outcome_relation_binding,
|
|
3118
|
+
)
|
|
3119
|
+
for candidate in self._history:
|
|
3120
|
+
preview.consider(candidate)
|
|
3121
|
+
if pareto_archive_snapshot_hash(preview.snapshot()) != (
|
|
3122
|
+
pareto_archive_snapshot_hash(current)
|
|
3123
|
+
):
|
|
3124
|
+
raise RuntimeError("campaign history cannot reproduce the live archive")
|
|
3125
|
+
for candidate in candidates:
|
|
3126
|
+
preview.consider(candidate)
|
|
3127
|
+
return preview
|
|
3128
|
+
|
|
3129
|
+
async def snapshot_archive(
|
|
3130
|
+
self,
|
|
3131
|
+
request: CampaignArchiveCutoffRequest,
|
|
3132
|
+
) -> CampaignArchiveCutoffReceipt:
|
|
3133
|
+
self._require_active()
|
|
3134
|
+
expected_prior = self._stage_receipts.get(request.generation - 1)
|
|
3135
|
+
if request.prior_stage_receipt_sha256 != (
|
|
3136
|
+
None if expected_prior is None else expected_prior.receipt_sha256
|
|
3137
|
+
):
|
|
3138
|
+
raise ValueError("archive request is not chained to the runtime stage")
|
|
3139
|
+
archive = self._archive_record()
|
|
3140
|
+
return CampaignArchiveCutoffReceipt(
|
|
3141
|
+
request_sha256=request.request_sha256,
|
|
3142
|
+
preparation_sha256=request.preparation_sha256,
|
|
3143
|
+
generation=request.generation,
|
|
3144
|
+
archive=archive,
|
|
3145
|
+
evidence=_object(
|
|
3146
|
+
{
|
|
3147
|
+
"cutoff_before_generation": request.generation,
|
|
3148
|
+
"archive_sha256": typed_json_sha256(archive),
|
|
3149
|
+
}
|
|
3150
|
+
),
|
|
3151
|
+
)
|
|
3152
|
+
|
|
3153
|
+
async def _optimizer_state_async(self, generation: int) -> OptimizerState:
|
|
3154
|
+
snapshot = self.archive.snapshot()
|
|
3155
|
+
cache = await self.composition.engine.evaluation_cache_snapshot()
|
|
3156
|
+
return OptimizerState(
|
|
3157
|
+
generation=generation,
|
|
3158
|
+
candidates=tuple(self._history),
|
|
3159
|
+
archive=snapshot,
|
|
3160
|
+
archive_snapshot_hash=pareto_archive_snapshot_hash(snapshot),
|
|
3161
|
+
unique_evaluations=_cache_misses(cache),
|
|
3162
|
+
logical_llm_calls=self._selector_calls,
|
|
3163
|
+
)
|
|
3164
|
+
|
|
3165
|
+
def _known_phenotypes(self) -> tuple[str, ...]:
|
|
3166
|
+
policy = self.composition.benchmark.phenotype_identity
|
|
3167
|
+
expected_policy = (policy.policy_id, policy.policy_version)
|
|
3168
|
+
known: set[str] = set()
|
|
3169
|
+
for candidate in self._history:
|
|
3170
|
+
configuration_sha256 = candidate.occurrence.configuration_hash
|
|
3171
|
+
value_sha256 = self._phenotype_identity_cache.get(configuration_sha256)
|
|
3172
|
+
if value_sha256 is None:
|
|
3173
|
+
# Engine candidates retain an immutable typed-JSON snapshot, while
|
|
3174
|
+
# benchmark phenotype policies are defined over ordinary detached
|
|
3175
|
+
# candidate values. Keep that boundary identical to catalog and
|
|
3176
|
+
# engine evaluation calls; policies must never need to understand
|
|
3177
|
+
# AgentEvolve's internal FrozenJson containers.
|
|
3178
|
+
identity = policy.identify(thaw_json(candidate.configuration))
|
|
3179
|
+
if type(identity) is not PhenotypeIdentity:
|
|
3180
|
+
raise TypeError(
|
|
3181
|
+
"phenotype policy must return exact PhenotypeIdentity"
|
|
3182
|
+
)
|
|
3183
|
+
PhenotypeIdentity.__post_init__(identity)
|
|
3184
|
+
if (identity.policy_id, identity.policy_version) != expected_policy:
|
|
3185
|
+
raise ValueError("phenotype policy returned a foreign identity law")
|
|
3186
|
+
value_sha256 = identity.value_sha256
|
|
3187
|
+
self._phenotype_identity_cache[configuration_sha256] = value_sha256
|
|
3188
|
+
known.add(value_sha256)
|
|
3189
|
+
return tuple(sorted(known))
|
|
3190
|
+
|
|
3191
|
+
def _validate_wave(
|
|
3192
|
+
self,
|
|
3193
|
+
*,
|
|
3194
|
+
wave: PortfolioVariationWaveRequest,
|
|
3195
|
+
build: CampaignPortfolioWaveContext,
|
|
3196
|
+
) -> None:
|
|
3197
|
+
if type(wave) is not PortfolioVariationWaveRequest:
|
|
3198
|
+
raise TypeError("wave factory must return PortfolioVariationWaveRequest")
|
|
3199
|
+
PortfolioVariationWaveRequest.__post_init__(wave)
|
|
3200
|
+
if (
|
|
3201
|
+
wave.parent is not build.parent
|
|
3202
|
+
or wave.generation != build.stage_request.step.generation
|
|
3203
|
+
or wave.selection_request.finite_variation_contract
|
|
3204
|
+
!= build.variation.contract
|
|
3205
|
+
or wave.selection_request.portfolio_size
|
|
3206
|
+
!= build.stage_request.step.offspring_per_parent
|
|
3207
|
+
):
|
|
3208
|
+
raise ValueError("wave factory escaped its trusted campaign inputs")
|
|
3209
|
+
try:
|
|
3210
|
+
resolve_campaign_selector_context_extension(
|
|
3211
|
+
trusted_context=build.evidence_context,
|
|
3212
|
+
selector_context=wave.selection_request.context,
|
|
3213
|
+
)
|
|
3214
|
+
except (TypeError, ValueError) as error:
|
|
3215
|
+
raise ValueError(
|
|
3216
|
+
"wave factory escaped its trusted campaign inputs"
|
|
3217
|
+
) from error
|
|
3218
|
+
|
|
3219
|
+
def _project_memory_estimand(
|
|
3220
|
+
self,
|
|
3221
|
+
build: CampaignPortfolioWaveContext,
|
|
3222
|
+
) -> CampaignPortfolioWaveContext:
|
|
3223
|
+
"""Install the sole dynamic core-owned selector-context projection."""
|
|
3224
|
+
|
|
3225
|
+
projector = self.memory_estimand_projector
|
|
3226
|
+
if projector is None:
|
|
3227
|
+
return build
|
|
3228
|
+
projection = projector.project(build)
|
|
3229
|
+
if projection is None:
|
|
3230
|
+
return build
|
|
3231
|
+
if type(projection) is not CampaignPortfolioMemoryEstimandProjection:
|
|
3232
|
+
raise TypeError(
|
|
3233
|
+
"memory estimand projector must return an exact projection or None"
|
|
3234
|
+
)
|
|
3235
|
+
CampaignPortfolioMemoryEstimandProjection.__post_init__(projection)
|
|
3236
|
+
projected_context = thaw_json(build.evidence_context)
|
|
3237
|
+
if type(projected_context) is not dict: # pragma: no cover - closed root.
|
|
3238
|
+
raise AssertionError("portfolio context did not thaw to an object")
|
|
3239
|
+
projected_context[MEMORY_ESTIMAND_CONTEXT_KEY] = thaw_json(
|
|
3240
|
+
projection.estimand_context
|
|
3241
|
+
)
|
|
3242
|
+
projected_context[MEMORY_ESTIMAND_STRATUM_SHA256_KEY] = (
|
|
3243
|
+
projection.estimand_stratum_sha256
|
|
3244
|
+
)
|
|
3245
|
+
return replace(
|
|
3246
|
+
build,
|
|
3247
|
+
evidence_context=_object(projected_context),
|
|
3248
|
+
)
|
|
3249
|
+
|
|
3250
|
+
@staticmethod
|
|
3251
|
+
def _selector_audit(
|
|
3252
|
+
*,
|
|
3253
|
+
generation: int,
|
|
3254
|
+
parent_slot: int,
|
|
3255
|
+
wave: PortfolioVariationWaveRequest,
|
|
3256
|
+
result: PortfolioVariationWaveResult,
|
|
3257
|
+
prior_audit_set_sha256: str,
|
|
3258
|
+
prompt_renderer: CampaignSelectorRequestPromptRenderer | None = None,
|
|
3259
|
+
) -> CampaignSelectorAuditReceipt:
|
|
3260
|
+
decision = result.receipt
|
|
3261
|
+
response = result.selection_decision_audit_record
|
|
3262
|
+
if response is None:
|
|
3263
|
+
raise RuntimeError("fresh portfolio result omitted decision audit")
|
|
3264
|
+
request_text = (
|
|
3265
|
+
render_portfolio_selection_prompt(wave.selection_request)
|
|
3266
|
+
if prompt_renderer is None
|
|
3267
|
+
else prompt_renderer.render(wave.selection_request)
|
|
3268
|
+
)
|
|
3269
|
+
if type(request_text) is not str or not request_text:
|
|
3270
|
+
raise TypeError("selector prompt renderer must return non-empty text")
|
|
3271
|
+
plaintext = _object(
|
|
3272
|
+
{
|
|
3273
|
+
"selector_call_id": wave.selection_request.call_id.value,
|
|
3274
|
+
"request_sha256": wave.selection_request.request_sha256,
|
|
3275
|
+
"decision_sha256": decision.decision_sha256,
|
|
3276
|
+
**encode_selector_audit_text("request_text", request_text),
|
|
3277
|
+
**encode_selector_audit_text(
|
|
3278
|
+
"response_text",
|
|
3279
|
+
_canonical_text(response),
|
|
3280
|
+
),
|
|
3281
|
+
"request_text_kind": "exact_framework_prompt",
|
|
3282
|
+
"response_text_kind": "trusted_structured_decision_projection",
|
|
3283
|
+
}
|
|
3284
|
+
)
|
|
3285
|
+
return CampaignSelectorAuditReceipt(
|
|
3286
|
+
generation=generation,
|
|
3287
|
+
parent_slot=parent_slot,
|
|
3288
|
+
selector_call_id=wave.selection_request.call_id.value,
|
|
3289
|
+
request_sha256=wave.selection_request.request_sha256,
|
|
3290
|
+
decision_sha256=decision.decision_sha256,
|
|
3291
|
+
trace_receipt_sha256=typed_json_sha256(plaintext),
|
|
3292
|
+
plaintext_audit=plaintext,
|
|
3293
|
+
prior_audit_set_sha256=prior_audit_set_sha256,
|
|
3294
|
+
execution_mode=SelectorAuditExecutionMode.FRESH,
|
|
3295
|
+
)
|
|
3296
|
+
|
|
3297
|
+
async def _portfolio_stage(
|
|
3298
|
+
self,
|
|
3299
|
+
request: CampaignStageRequest,
|
|
3300
|
+
*,
|
|
3301
|
+
cache_before: int,
|
|
3302
|
+
) -> CampaignStageReceipt:
|
|
3303
|
+
step = request.step
|
|
3304
|
+
state = await self._optimizer_state_async(step.generation - 1)
|
|
3305
|
+
selection = self.parent_selector.select(
|
|
3306
|
+
state,
|
|
3307
|
+
task_sha256=self.task_sha256,
|
|
3308
|
+
parent_count=step.parent_count,
|
|
3309
|
+
rotation_index=(step.generation - 1) // 2,
|
|
3310
|
+
progress=tuple(self._parent_selection_progress),
|
|
3311
|
+
archive_utility=request.archive_utility,
|
|
3312
|
+
)
|
|
3313
|
+
if type(selection) is not CampaignParentSelection:
|
|
3314
|
+
raise TypeError("parent selector must return CampaignParentSelection")
|
|
3315
|
+
CampaignParentSelection.__post_init__(selection)
|
|
3316
|
+
if (
|
|
3317
|
+
len(selection.parents) != step.parent_count
|
|
3318
|
+
or len(selection.lanes) != step.parent_count
|
|
3319
|
+
or len(selection.decision_slots) != step.parent_count
|
|
3320
|
+
):
|
|
3321
|
+
raise ValueError("parent selector did not satisfy the prepared stage")
|
|
3322
|
+
assert self._memory is not None
|
|
3323
|
+
if any(
|
|
3324
|
+
value not in self._test_eligible_reflection_hashes
|
|
3325
|
+
for value in request.test_eligible_reflection_receipt_sha256s
|
|
3326
|
+
):
|
|
3327
|
+
raise RuntimeError(
|
|
3328
|
+
"portfolio stage names reflection evidence not admitted for testing"
|
|
3329
|
+
)
|
|
3330
|
+
known = self._known_phenotypes()
|
|
3331
|
+
builds: list[CampaignPortfolioWaveContext] = []
|
|
3332
|
+
build_hashes: list[tuple[str, str]] = []
|
|
3333
|
+
wave_preparations: list[CampaignPortfolioWavePreparationReceipt] = []
|
|
3334
|
+
lanes_by_id = {lane.lane_id: lane for lane in selection.lanes}
|
|
3335
|
+
lane_inputs = tuple(
|
|
3336
|
+
(
|
|
3337
|
+
parent_slot,
|
|
3338
|
+
decision_slot,
|
|
3339
|
+
lanes_by_id[decision_slot.lane_id],
|
|
3340
|
+
)
|
|
3341
|
+
for parent_slot, decision_slot in enumerate(selection.decision_slots)
|
|
3342
|
+
)
|
|
3343
|
+
base_variations = tuple(
|
|
3344
|
+
self.workload_ports.catalog.bind(
|
|
3345
|
+
self.prepared.benchmark_session.benchmark,
|
|
3346
|
+
parent_lane.parent.configuration,
|
|
3347
|
+
known,
|
|
3348
|
+
)
|
|
3349
|
+
for _, _, parent_lane in lane_inputs
|
|
3350
|
+
)
|
|
3351
|
+
variation_envelope_evidence: dict[str, object] | None = None
|
|
3352
|
+
variation_envelope_trace: dict[str, object] | None = None
|
|
3353
|
+
variations = base_variations
|
|
3354
|
+
if self.variation_envelope is not None:
|
|
3355
|
+
envelope_request = CampaignVariationEnvelopeRequest(
|
|
3356
|
+
campaign_scope_sha256=self.task_sha256,
|
|
3357
|
+
generation=step.generation,
|
|
3358
|
+
evaluation_slots_per_lane=step.offspring_per_parent,
|
|
3359
|
+
state=state,
|
|
3360
|
+
archive_utility=request.archive_utility,
|
|
3361
|
+
lanes=tuple(
|
|
3362
|
+
sorted(
|
|
3363
|
+
(
|
|
3364
|
+
CampaignVariationEnvelopeLane(
|
|
3365
|
+
lane_id=parent_lane.lane_id,
|
|
3366
|
+
parent=parent_lane.parent,
|
|
3367
|
+
base_variation=variation,
|
|
3368
|
+
)
|
|
3369
|
+
for (_, _, parent_lane), variation in zip(
|
|
3370
|
+
lane_inputs,
|
|
3371
|
+
base_variations,
|
|
3372
|
+
strict=True,
|
|
3373
|
+
)
|
|
3374
|
+
),
|
|
3375
|
+
key=lambda value: value.lane_id,
|
|
3376
|
+
)
|
|
3377
|
+
),
|
|
3378
|
+
)
|
|
3379
|
+
envelope_result = self.variation_envelope.enrich(envelope_request)
|
|
3380
|
+
validate_campaign_variation_envelope_result(
|
|
3381
|
+
policy=self.variation_envelope,
|
|
3382
|
+
request=envelope_request,
|
|
3383
|
+
result=envelope_result,
|
|
3384
|
+
)
|
|
3385
|
+
variation_by_lane = {
|
|
3386
|
+
value.lane_id: value.variation for value in envelope_result.lanes
|
|
3387
|
+
}
|
|
3388
|
+
variations = tuple(
|
|
3389
|
+
variation_by_lane[parent_lane.lane_id]
|
|
3390
|
+
for _, _, parent_lane in lane_inputs
|
|
3391
|
+
)
|
|
3392
|
+
variation_envelope_evidence = (
|
|
3393
|
+
campaign_variation_envelope_context_record(envelope_result)
|
|
3394
|
+
)
|
|
3395
|
+
variation_envelope_trace = campaign_variation_envelope_trace_record(
|
|
3396
|
+
request=envelope_request,
|
|
3397
|
+
result=envelope_result,
|
|
3398
|
+
)
|
|
3399
|
+
|
|
3400
|
+
for (
|
|
3401
|
+
parent_slot,
|
|
3402
|
+
decision_slot,
|
|
3403
|
+
parent_lane,
|
|
3404
|
+
), base_variation, variation in zip(
|
|
3405
|
+
lane_inputs,
|
|
3406
|
+
base_variations,
|
|
3407
|
+
variations,
|
|
3408
|
+
strict=True,
|
|
3409
|
+
):
|
|
3410
|
+
parent_lane = lanes_by_id[decision_slot.lane_id]
|
|
3411
|
+
parent = parent_lane.parent
|
|
3412
|
+
# Workload evidence is owned by the workload catalog authority and
|
|
3413
|
+
# therefore remains bound to its exact issued base view. External
|
|
3414
|
+
# proposal experts carry their own authenticated envelope evidence;
|
|
3415
|
+
# requiring a workload adapter to reissue every generic expert
|
|
3416
|
+
# union would couple the inverted API back to individual experts.
|
|
3417
|
+
evidence_context = self.workload_ports.evidence.context(
|
|
3418
|
+
self.prepared.benchmark_session,
|
|
3419
|
+
parent.configuration,
|
|
3420
|
+
base_variation,
|
|
3421
|
+
self._memory,
|
|
3422
|
+
)
|
|
3423
|
+
workload_context_sha256 = typed_json_sha256(evidence_context)
|
|
3424
|
+
parent_measurement = (
|
|
3425
|
+
None
|
|
3426
|
+
if self.parent_measurement_projection is None
|
|
3427
|
+
else bind_parent_measurement(
|
|
3428
|
+
candidate=parent,
|
|
3429
|
+
variation=variation,
|
|
3430
|
+
projection=self.parent_measurement_projection,
|
|
3431
|
+
)
|
|
3432
|
+
)
|
|
3433
|
+
evidence_context = attach_parent_measurement_to_context(
|
|
3434
|
+
evidence_context,
|
|
3435
|
+
parent_measurement,
|
|
3436
|
+
)
|
|
3437
|
+
portfolio_generations = tuple(
|
|
3438
|
+
step.generation
|
|
3439
|
+
for step in self.prepared.schedule.steps
|
|
3440
|
+
if step.kind is CampaignGenerationKind.PORTFOLIO
|
|
3441
|
+
)
|
|
3442
|
+
evidence_context = attach_campaign_search_phase_context(
|
|
3443
|
+
evidence_context,
|
|
3444
|
+
campaign_search_phase_context(
|
|
3445
|
+
campaign_generation=request.step.generation,
|
|
3446
|
+
portfolio_generations=portfolio_generations,
|
|
3447
|
+
),
|
|
3448
|
+
)
|
|
3449
|
+
if variation_envelope_evidence is not None:
|
|
3450
|
+
envelope_context = thaw_json(evidence_context)
|
|
3451
|
+
if type(envelope_context) is not dict:
|
|
3452
|
+
raise TypeError("campaign evidence context must be an object")
|
|
3453
|
+
if CAMPAIGN_VARIATION_ENVELOPE_KEY in envelope_context:
|
|
3454
|
+
raise ValueError(
|
|
3455
|
+
"base context uses the reserved variation-envelope key"
|
|
3456
|
+
)
|
|
3457
|
+
envelope_context[CAMPAIGN_VARIATION_ENVELOPE_KEY] = (
|
|
3458
|
+
variation_envelope_evidence
|
|
3459
|
+
)
|
|
3460
|
+
evidence_context = _object(envelope_context)
|
|
3461
|
+
evidence_cards = self.workload_ports.evidence.cards(
|
|
3462
|
+
self.prepared.benchmark_session,
|
|
3463
|
+
parent.configuration,
|
|
3464
|
+
base_variation,
|
|
3465
|
+
self._memory,
|
|
3466
|
+
)
|
|
3467
|
+
build = CampaignPortfolioWaveContext(
|
|
3468
|
+
prepared=self.prepared,
|
|
3469
|
+
stage_request=request,
|
|
3470
|
+
parent_slot=parent_slot,
|
|
3471
|
+
parent=parent,
|
|
3472
|
+
variation=variation,
|
|
3473
|
+
evidence_context=evidence_context,
|
|
3474
|
+
evidence_cards=evidence_cards,
|
|
3475
|
+
memory=self._memory,
|
|
3476
|
+
parent_measurement=parent_measurement,
|
|
3477
|
+
test_eligible_reflections=tuple(
|
|
3478
|
+
(
|
|
3479
|
+
receipt_sha256,
|
|
3480
|
+
self._reflection_results[receipt_sha256],
|
|
3481
|
+
)
|
|
3482
|
+
for receipt_sha256 in (
|
|
3483
|
+
request.test_eligible_reflection_receipt_sha256s
|
|
3484
|
+
)
|
|
3485
|
+
),
|
|
3486
|
+
parent_lane=parent_lane,
|
|
3487
|
+
decision_slot=decision_slot,
|
|
3488
|
+
)
|
|
3489
|
+
archive_projector = self.archive_context_projector
|
|
3490
|
+
if archive_projector is not None:
|
|
3491
|
+
projection = archive_projector.project(
|
|
3492
|
+
archive_utility=request.archive_utility,
|
|
3493
|
+
parent=parent,
|
|
3494
|
+
)
|
|
3495
|
+
if type(projection) is not (CampaignPortfolioArchiveContextProjection):
|
|
3496
|
+
raise TypeError(
|
|
3497
|
+
"archive context projector must return an exact projection"
|
|
3498
|
+
)
|
|
3499
|
+
CampaignPortfolioArchiveContextProjection.__post_init__(projection)
|
|
3500
|
+
if (
|
|
3501
|
+
projection.projector_id != archive_projector.projector_id
|
|
3502
|
+
or projection.projector_version
|
|
3503
|
+
!= archive_projector.projector_version
|
|
3504
|
+
or projection.definition_sha256
|
|
3505
|
+
!= archive_projector.definition_sha256
|
|
3506
|
+
or projection.archive_utility_snapshot_sha256
|
|
3507
|
+
!= request.archive_utility.snapshot_sha256
|
|
3508
|
+
or projection.parent_configuration_sha256
|
|
3509
|
+
!= parent.occurrence.configuration_hash
|
|
3510
|
+
):
|
|
3511
|
+
raise ValueError(
|
|
3512
|
+
"archive context projection differs from its trusted inputs"
|
|
3513
|
+
)
|
|
3514
|
+
base_context = thaw_json(build.evidence_context)
|
|
3515
|
+
if type(base_context) is not dict: # pragma: no cover - closed root.
|
|
3516
|
+
raise AssertionError("portfolio context did not thaw to an object")
|
|
3517
|
+
if CAMPAIGN_ARCHIVE_CONTEXT_KEY in base_context:
|
|
3518
|
+
raise ValueError(
|
|
3519
|
+
"base context uses the reserved campaign-archive key"
|
|
3520
|
+
)
|
|
3521
|
+
base_context[CAMPAIGN_ARCHIVE_CONTEXT_KEY] = projection.to_record()
|
|
3522
|
+
build = replace(
|
|
3523
|
+
build,
|
|
3524
|
+
evidence_context=_object(base_context),
|
|
3525
|
+
archive_context=projection,
|
|
3526
|
+
)
|
|
3527
|
+
enricher = self.context_enricher
|
|
3528
|
+
if enricher is not None:
|
|
3529
|
+
contextual_history = enricher.enrich(build)
|
|
3530
|
+
if (
|
|
3531
|
+
type(contextual_history) is not FrozenJsonObject
|
|
3532
|
+
or freeze_json(contextual_history) is not contextual_history
|
|
3533
|
+
):
|
|
3534
|
+
raise TypeError("context enricher must return a frozen object")
|
|
3535
|
+
base_context = thaw_json(build.evidence_context)
|
|
3536
|
+
if type(base_context) is not dict: # pragma: no cover - closed root.
|
|
3537
|
+
raise AssertionError("portfolio context did not thaw to an object")
|
|
3538
|
+
if CAMPAIGN_CONTEXTUAL_HISTORY_KEY in base_context:
|
|
3539
|
+
raise ValueError(
|
|
3540
|
+
"base context uses the reserved contextual-history key"
|
|
3541
|
+
)
|
|
3542
|
+
base_context[CAMPAIGN_CONTEXTUAL_HISTORY_KEY] = thaw_json(
|
|
3543
|
+
contextual_history
|
|
3544
|
+
)
|
|
3545
|
+
enriched_context = _object(base_context)
|
|
3546
|
+
build = replace(build, evidence_context=enriched_context)
|
|
3547
|
+
pre_memory_projection_context_sha256 = typed_json_sha256(
|
|
3548
|
+
build.evidence_context
|
|
3549
|
+
)
|
|
3550
|
+
build = self._project_memory_estimand(build)
|
|
3551
|
+
builds.append(build)
|
|
3552
|
+
build_hashes.append(
|
|
3553
|
+
(
|
|
3554
|
+
workload_context_sha256,
|
|
3555
|
+
pre_memory_projection_context_sha256,
|
|
3556
|
+
)
|
|
3557
|
+
)
|
|
3558
|
+
|
|
3559
|
+
planner = self.contextual_search_planner
|
|
3560
|
+
contextual_search_plan = None
|
|
3561
|
+
standalone_frontier_targets: tuple[
|
|
3562
|
+
CampaignPortfolioFrontierTarget, ...
|
|
3563
|
+
] | None = None
|
|
3564
|
+
if planner is not None:
|
|
3565
|
+
contextual_search_plan = planner.plan(tuple(builds))
|
|
3566
|
+
contracts = {
|
|
3567
|
+
value.slice_id: value for value in contextual_search_plan.contracts
|
|
3568
|
+
}
|
|
3569
|
+
frontier_targets = {
|
|
3570
|
+
value.lane_id: value
|
|
3571
|
+
for value in contextual_search_plan.frontier_targets
|
|
3572
|
+
}
|
|
3573
|
+
lane_ids = tuple(value.parent_lane.lane_id for value in builds)
|
|
3574
|
+
if set(contracts) != set(lane_ids) or set(frontier_targets) != set(
|
|
3575
|
+
lane_ids
|
|
3576
|
+
):
|
|
3577
|
+
raise ValueError(
|
|
3578
|
+
"contextual plan differs from the campaign parent lanes"
|
|
3579
|
+
)
|
|
3580
|
+
targeted_builds = []
|
|
3581
|
+
for value in builds:
|
|
3582
|
+
lane_id = value.parent_lane.lane_id
|
|
3583
|
+
target = frontier_targets[lane_id]
|
|
3584
|
+
evidence = thaw_json(value.evidence_context)
|
|
3585
|
+
if type(evidence) is not dict:
|
|
3586
|
+
raise TypeError("campaign evidence context must be an object")
|
|
3587
|
+
if CAMPAIGN_FRONTIER_TARGET_KEY in evidence:
|
|
3588
|
+
raise ValueError(
|
|
3589
|
+
"base context uses the reserved campaign frontier-target key"
|
|
3590
|
+
)
|
|
3591
|
+
evidence[CAMPAIGN_FRONTIER_TARGET_KEY] = target.to_record()
|
|
3592
|
+
targeted_builds.append(
|
|
3593
|
+
replace(
|
|
3594
|
+
value,
|
|
3595
|
+
evidence_context=_object(evidence),
|
|
3596
|
+
contextual_allocation=contracts[lane_id],
|
|
3597
|
+
frontier_target=target,
|
|
3598
|
+
)
|
|
3599
|
+
)
|
|
3600
|
+
builds = targeted_builds
|
|
3601
|
+
elif self.frontier_target_allocator is not None:
|
|
3602
|
+
allocator = self.frontier_target_allocator
|
|
3603
|
+
lanes = tuple(
|
|
3604
|
+
sorted(
|
|
3605
|
+
(
|
|
3606
|
+
(value.parent_lane.lane_id, value.parent)
|
|
3607
|
+
for value in builds
|
|
3608
|
+
),
|
|
3609
|
+
key=lambda value: value[0],
|
|
3610
|
+
)
|
|
3611
|
+
)
|
|
3612
|
+
standalone_frontier_targets = allocator.allocate(
|
|
3613
|
+
archive_utility=request.archive_utility,
|
|
3614
|
+
lanes=lanes,
|
|
3615
|
+
)
|
|
3616
|
+
if type(standalone_frontier_targets) is not tuple or any(
|
|
3617
|
+
type(value) is not CampaignPortfolioFrontierTarget
|
|
3618
|
+
for value in standalone_frontier_targets
|
|
3619
|
+
):
|
|
3620
|
+
raise TypeError(
|
|
3621
|
+
"frontier target allocator must return exact target receipts"
|
|
3622
|
+
)
|
|
3623
|
+
by_lane = {
|
|
3624
|
+
value.lane_id: value for value in standalone_frontier_targets
|
|
3625
|
+
}
|
|
3626
|
+
lane_ids = tuple(value[0] for value in lanes)
|
|
3627
|
+
if tuple(sorted(by_lane)) != lane_ids:
|
|
3628
|
+
raise ValueError(
|
|
3629
|
+
"standalone frontier targets differ from the campaign lanes"
|
|
3630
|
+
)
|
|
3631
|
+
targeted_builds = []
|
|
3632
|
+
for value in builds:
|
|
3633
|
+
lane_id = value.parent_lane.lane_id
|
|
3634
|
+
target = by_lane[lane_id]
|
|
3635
|
+
target.__post_init__()
|
|
3636
|
+
evidence = thaw_json(value.evidence_context)
|
|
3637
|
+
if type(evidence) is not dict:
|
|
3638
|
+
raise TypeError("campaign evidence context must be an object")
|
|
3639
|
+
if CAMPAIGN_FRONTIER_TARGET_KEY in evidence:
|
|
3640
|
+
raise ValueError(
|
|
3641
|
+
"base context uses the reserved campaign frontier-target key"
|
|
3642
|
+
)
|
|
3643
|
+
evidence[CAMPAIGN_FRONTIER_TARGET_KEY] = target.to_record()
|
|
3644
|
+
targeted_builds.append(
|
|
3645
|
+
replace(
|
|
3646
|
+
value,
|
|
3647
|
+
evidence_context=_object(evidence),
|
|
3648
|
+
frontier_target=target,
|
|
3649
|
+
)
|
|
3650
|
+
)
|
|
3651
|
+
builds = targeted_builds
|
|
3652
|
+
|
|
3653
|
+
factory = self.wave_factory
|
|
3654
|
+
if isinstance(factory, CampaignPortfolioWaveBatchFactory):
|
|
3655
|
+
built_waves = factory.build_batch(tuple(builds))
|
|
3656
|
+
if type(built_waves) is not tuple:
|
|
3657
|
+
raise TypeError("batch wave factory must return an exact tuple")
|
|
3658
|
+
if len(built_waves) != len(builds):
|
|
3659
|
+
raise ValueError("batch wave factory must preserve lane cardinality")
|
|
3660
|
+
else:
|
|
3661
|
+
built_waves = tuple(factory.build(build) for build in builds)
|
|
3662
|
+
if any(type(wave) is not PortfolioVariationWaveRequest for wave in built_waves):
|
|
3663
|
+
raise TypeError("wave factory returned a foreign wave request")
|
|
3664
|
+
|
|
3665
|
+
waves: list[PortfolioVariationWaveRequest] = []
|
|
3666
|
+
for build, wave, hashes in zip(
|
|
3667
|
+
builds,
|
|
3668
|
+
built_waves,
|
|
3669
|
+
build_hashes,
|
|
3670
|
+
strict=True,
|
|
3671
|
+
):
|
|
3672
|
+
workload_context_sha256, pre_memory_projection_context_sha256 = hashes
|
|
3673
|
+
self._validate_wave(wave=wave, build=build)
|
|
3674
|
+
waves.append(wave)
|
|
3675
|
+
wave_preparations.append(
|
|
3676
|
+
_validated_wave_preparation_receipt(
|
|
3677
|
+
build=build,
|
|
3678
|
+
wave=wave,
|
|
3679
|
+
workload_context_sha256=workload_context_sha256,
|
|
3680
|
+
pre_memory_projection_context_sha256=(
|
|
3681
|
+
pre_memory_projection_context_sha256
|
|
3682
|
+
),
|
|
3683
|
+
)
|
|
3684
|
+
)
|
|
3685
|
+
if len({wave.selection_request.call_id for wave in waves}) != len(waves):
|
|
3686
|
+
raise ValueError("portfolio stage reused a selector call ID")
|
|
3687
|
+
|
|
3688
|
+
# Preparation evidence is prospective audit state, not a sealed stage
|
|
3689
|
+
# result. Retain it even when provider execution or a later transaction
|
|
3690
|
+
# barrier fails. An injected observer may durably journal each receipt;
|
|
3691
|
+
# any observer failure aborts before gather_concurrent_stage can dispatch.
|
|
3692
|
+
self._wave_preparation_receipts.extend(wave_preparations)
|
|
3693
|
+
observer = self.wave_preparation_observer
|
|
3694
|
+
if observer is not None:
|
|
3695
|
+
for receipt in wave_preparations:
|
|
3696
|
+
observer.record_prepared_wave(receipt)
|
|
3697
|
+
|
|
3698
|
+
pending_results = await gather_concurrent_stage(
|
|
3699
|
+
self.composition.portfolio.run(
|
|
3700
|
+
wave,
|
|
3701
|
+
defer_memory_credit=True,
|
|
3702
|
+
)
|
|
3703
|
+
for wave in waves
|
|
3704
|
+
)
|
|
3705
|
+
if any(
|
|
3706
|
+
type(value) is not PortfolioVariationWaveResult for value in pending_results
|
|
3707
|
+
):
|
|
3708
|
+
raise TypeError("portfolio service returned a foreign result")
|
|
3709
|
+
audits = tuple(
|
|
3710
|
+
self._selector_audit(
|
|
3711
|
+
generation=step.generation,
|
|
3712
|
+
parent_slot=slot,
|
|
3713
|
+
wave=wave,
|
|
3714
|
+
result=result,
|
|
3715
|
+
prior_audit_set_sha256=request.prior_selector_audit_set_sha256,
|
|
3716
|
+
prompt_renderer=self.selector_request_prompt_renderer,
|
|
3717
|
+
)
|
|
3718
|
+
for slot, (wave, result) in enumerate(
|
|
3719
|
+
zip(waves, pending_results, strict=True)
|
|
3720
|
+
)
|
|
3721
|
+
)
|
|
3722
|
+
memory_credit_preparation = (
|
|
3723
|
+
self.composition.portfolio.prepare_pending_memory_credit_batch(
|
|
3724
|
+
pending_results
|
|
3725
|
+
)
|
|
3726
|
+
)
|
|
3727
|
+
results = memory_credit_preparation.prepared_results
|
|
3728
|
+
memory_credit_batch = memory_credit_preparation.batch_receipt
|
|
3729
|
+
candidates = tuple(
|
|
3730
|
+
candidate for result in results for candidate in result.candidates
|
|
3731
|
+
)
|
|
3732
|
+
scored_candidate_count = sum(
|
|
3733
|
+
len(result.scored_candidates) for result in results
|
|
3734
|
+
)
|
|
3735
|
+
infeasible_candidate_count = sum(
|
|
3736
|
+
len(result.infeasible_candidates) for result in results
|
|
3737
|
+
)
|
|
3738
|
+
if scored_candidate_count + infeasible_candidate_count != len(candidates):
|
|
3739
|
+
raise RuntimeError(
|
|
3740
|
+
"portfolio disposition partition does not cover ranked ITT candidates"
|
|
3741
|
+
)
|
|
3742
|
+
prior_archive_decision_count = len(self.archive.decisions)
|
|
3743
|
+
preview_archive = self._preview_archive(candidates)
|
|
3744
|
+
archive_decisions = preview_archive.decisions[prior_archive_decision_count:]
|
|
3745
|
+
prior_memory = self._memory
|
|
3746
|
+
updater = self.outcome_updater
|
|
3747
|
+
learning = self.learning_lifecycle
|
|
3748
|
+
wave_request_sha256s = tuple(
|
|
3749
|
+
value.selection_request.request_sha256 for value in waves
|
|
3750
|
+
)
|
|
3751
|
+
result_receipt_sha256s = tuple(
|
|
3752
|
+
value.receipt.receipt_sha256 for value in results
|
|
3753
|
+
)
|
|
3754
|
+
outcome_preparation: CampaignPortfolioOutcomePreparation | None = None
|
|
3755
|
+
learning_preparation: CampaignPortfolioLearningPreparation | None = None
|
|
3756
|
+
try:
|
|
3757
|
+
if updater is not None:
|
|
3758
|
+
outcome_preparation = await updater.prepare_update(
|
|
3759
|
+
request,
|
|
3760
|
+
tuple(waves),
|
|
3761
|
+
results,
|
|
3762
|
+
prior_memory,
|
|
3763
|
+
)
|
|
3764
|
+
if type(outcome_preparation) is not CampaignPortfolioOutcomePreparation:
|
|
3765
|
+
raise TypeError(
|
|
3766
|
+
"outcome updater must return exact preparation evidence"
|
|
3767
|
+
)
|
|
3768
|
+
CampaignPortfolioOutcomePreparation.__post_init__(outcome_preparation)
|
|
3769
|
+
if (
|
|
3770
|
+
outcome_preparation.request_sha256 != request.request_sha256
|
|
3771
|
+
or outcome_preparation.generation != step.generation
|
|
3772
|
+
or outcome_preparation.wave_request_sha256s != wave_request_sha256s
|
|
3773
|
+
or outcome_preparation.result_receipt_sha256s
|
|
3774
|
+
!= result_receipt_sha256s
|
|
3775
|
+
or outcome_preparation.prior_memory_sha256
|
|
3776
|
+
!= typed_json_sha256(prior_memory)
|
|
3777
|
+
):
|
|
3778
|
+
raise ValueError("outcome preparation differs from the stage")
|
|
3779
|
+
updated_memory = outcome_preparation.updated_memory
|
|
3780
|
+
else:
|
|
3781
|
+
updated_memory = prior_memory
|
|
3782
|
+
if learning is not None:
|
|
3783
|
+
learning_preparation = (
|
|
3784
|
+
await learning.prepare_portfolio_generation_close(
|
|
3785
|
+
request,
|
|
3786
|
+
tuple(waves),
|
|
3787
|
+
results,
|
|
3788
|
+
memory_credit_preparation,
|
|
3789
|
+
)
|
|
3790
|
+
)
|
|
3791
|
+
if type(learning_preparation) is not (
|
|
3792
|
+
CampaignPortfolioLearningPreparation
|
|
3793
|
+
):
|
|
3794
|
+
raise TypeError(
|
|
3795
|
+
"learning lifecycle must return exact preparation evidence"
|
|
3796
|
+
)
|
|
3797
|
+
CampaignPortfolioLearningPreparation.__post_init__(learning_preparation)
|
|
3798
|
+
if (
|
|
3799
|
+
learning_preparation.request_sha256 != request.request_sha256
|
|
3800
|
+
or learning_preparation.generation != step.generation
|
|
3801
|
+
or learning_preparation.wave_request_sha256s != wave_request_sha256s
|
|
3802
|
+
or learning_preparation.result_receipt_sha256s
|
|
3803
|
+
!= result_receipt_sha256s
|
|
3804
|
+
or learning_preparation.memory_credit_preparation_sha256
|
|
3805
|
+
!= memory_credit_preparation.preparation_sha256
|
|
3806
|
+
):
|
|
3807
|
+
raise ValueError("learning preparation differs from the stage")
|
|
3808
|
+
cache_after = _cache_misses(
|
|
3809
|
+
await self.composition.engine.evaluation_cache_snapshot()
|
|
3810
|
+
)
|
|
3811
|
+
receipt = CampaignStageReceipt(
|
|
3812
|
+
request_sha256=request.request_sha256,
|
|
3813
|
+
preparation_sha256=request.preparation_sha256,
|
|
3814
|
+
generation=step.generation,
|
|
3815
|
+
kind=step.kind,
|
|
3816
|
+
candidate_occurrence_count=len(candidates),
|
|
3817
|
+
unique_evaluation_count=cache_after - cache_before,
|
|
3818
|
+
selector_audits=audits,
|
|
3819
|
+
result=_object(
|
|
3820
|
+
{
|
|
3821
|
+
"parent_selection": thaw_json(selection.evidence),
|
|
3822
|
+
"parent_lanes": [
|
|
3823
|
+
value.to_record() for value in selection.lanes
|
|
3824
|
+
],
|
|
3825
|
+
"decision_slots": [
|
|
3826
|
+
value.to_record() for value in selection.decision_slots
|
|
3827
|
+
],
|
|
3828
|
+
"portfolio_wave_receipts": [
|
|
3829
|
+
value.receipt.to_record() for value in results
|
|
3830
|
+
],
|
|
3831
|
+
"variation_envelope_trace_receipt": (
|
|
3832
|
+
variation_envelope_trace
|
|
3833
|
+
),
|
|
3834
|
+
"candidates": [
|
|
3835
|
+
_candidate_record(candidate) for candidate in candidates
|
|
3836
|
+
],
|
|
3837
|
+
"ranked_itt_candidate_count": len(candidates),
|
|
3838
|
+
"scored_candidate_count": scored_candidate_count,
|
|
3839
|
+
"candidate_infeasible_count": infeasible_candidate_count,
|
|
3840
|
+
"archive_candidate_decisions": [
|
|
3841
|
+
_archive_decision_record(value)
|
|
3842
|
+
for value in archive_decisions
|
|
3843
|
+
],
|
|
3844
|
+
"archive_candidate_consideration_count": len(candidates),
|
|
3845
|
+
"candidate_infeasibility_recourse": (
|
|
3846
|
+
"retain_ranked_itt_reject_from_archive_no_resampling"
|
|
3847
|
+
),
|
|
3848
|
+
"archive_after": thaw_json(
|
|
3849
|
+
self._archive_record_for(preview_archive)
|
|
3850
|
+
),
|
|
3851
|
+
"memory_before_sha256": typed_json_sha256(prior_memory),
|
|
3852
|
+
"memory_after_sha256": typed_json_sha256(updated_memory),
|
|
3853
|
+
"memory_projection_updated": updater is not None,
|
|
3854
|
+
"outcome_update_preparation": (
|
|
3855
|
+
None
|
|
3856
|
+
if outcome_preparation is None
|
|
3857
|
+
else outcome_preparation.to_record()
|
|
3858
|
+
),
|
|
3859
|
+
"context_enrichment_applied": (
|
|
3860
|
+
self.context_enricher is not None
|
|
3861
|
+
),
|
|
3862
|
+
"archive_context_projection": (
|
|
3863
|
+
None
|
|
3864
|
+
if self.archive_context_projector is None
|
|
3865
|
+
else {
|
|
3866
|
+
"reserved_key": CAMPAIGN_ARCHIVE_CONTEXT_KEY,
|
|
3867
|
+
"projector_id": (
|
|
3868
|
+
self.archive_context_projector.projector_id
|
|
3869
|
+
),
|
|
3870
|
+
"projector_version": (
|
|
3871
|
+
self.archive_context_projector.projector_version
|
|
3872
|
+
),
|
|
3873
|
+
"definition_sha256": (
|
|
3874
|
+
self.archive_context_projector.definition_sha256
|
|
3875
|
+
),
|
|
3876
|
+
}
|
|
3877
|
+
),
|
|
3878
|
+
"contextual_search_plan": (
|
|
3879
|
+
None
|
|
3880
|
+
if contextual_search_plan is None
|
|
3881
|
+
else contextual_search_plan.to_record()
|
|
3882
|
+
),
|
|
3883
|
+
**(
|
|
3884
|
+
{}
|
|
3885
|
+
if standalone_frontier_targets is None
|
|
3886
|
+
else {
|
|
3887
|
+
"standalone_frontier_targets": [
|
|
3888
|
+
value.to_record()
|
|
3889
|
+
for value in standalone_frontier_targets
|
|
3890
|
+
]
|
|
3891
|
+
}
|
|
3892
|
+
),
|
|
3893
|
+
"closed_loop_learning": (
|
|
3894
|
+
None
|
|
3895
|
+
if learning_preparation is None
|
|
3896
|
+
else learning_preparation.to_record()
|
|
3897
|
+
),
|
|
3898
|
+
"memory_credit_preparation_sha256": (
|
|
3899
|
+
memory_credit_preparation.preparation_sha256
|
|
3900
|
+
),
|
|
3901
|
+
"memory_credit_batch": (
|
|
3902
|
+
None
|
|
3903
|
+
if memory_credit_batch is None
|
|
3904
|
+
else memory_credit_batch.to_record()
|
|
3905
|
+
),
|
|
3906
|
+
}
|
|
3907
|
+
),
|
|
3908
|
+
)
|
|
3909
|
+
except BaseException:
|
|
3910
|
+
if (
|
|
3911
|
+
learning is not None
|
|
3912
|
+
and type(learning_preparation) is CampaignPortfolioLearningPreparation
|
|
3913
|
+
):
|
|
3914
|
+
learning.abort_portfolio_generation_close(learning_preparation)
|
|
3915
|
+
if (
|
|
3916
|
+
updater is not None
|
|
3917
|
+
and type(outcome_preparation) is CampaignPortfolioOutcomePreparation
|
|
3918
|
+
):
|
|
3919
|
+
updater.abort_update(outcome_preparation)
|
|
3920
|
+
raise
|
|
3921
|
+
|
|
3922
|
+
# All potentially fallible computation and receipt construction has
|
|
3923
|
+
# completed. The following methods are synchronous exact-publication
|
|
3924
|
+
# operations whose protocol forbids I/O and new validation.
|
|
3925
|
+
committed_results, committed_batch = (
|
|
3926
|
+
self.composition.portfolio.commit_prepared_memory_credit_batch(
|
|
3927
|
+
memory_credit_preparation
|
|
3928
|
+
)
|
|
3929
|
+
)
|
|
3930
|
+
if committed_results != results or committed_batch != memory_credit_batch:
|
|
3931
|
+
raise RuntimeError("memory commit differs from its stage preparation")
|
|
3932
|
+
if updater is not None:
|
|
3933
|
+
assert outcome_preparation is not None
|
|
3934
|
+
updater.commit_update(outcome_preparation)
|
|
3935
|
+
if learning is not None:
|
|
3936
|
+
assert learning_preparation is not None
|
|
3937
|
+
learning.commit_portfolio_generation_close(learning_preparation)
|
|
3938
|
+
self._history.extend(candidates)
|
|
3939
|
+
self.archive = preview_archive
|
|
3940
|
+
self._memory = updated_memory
|
|
3941
|
+
self._selector_calls += len(waves)
|
|
3942
|
+
self._portfolio_waves[step.generation] = tuple(zip(waves, results, strict=True))
|
|
3943
|
+
return receipt
|
|
3944
|
+
|
|
3945
|
+
async def _recombination_stage(
|
|
3946
|
+
self,
|
|
3947
|
+
request: CampaignStageRequest,
|
|
3948
|
+
*,
|
|
3949
|
+
cache_before: int,
|
|
3950
|
+
) -> CampaignStageReceipt:
|
|
3951
|
+
step = request.step
|
|
3952
|
+
source_generation = step.source_portfolio_generation
|
|
3953
|
+
assert source_generation is not None
|
|
3954
|
+
source = self._portfolio_waves.get(source_generation)
|
|
3955
|
+
source_stage = self._stage_receipts.get(source_generation)
|
|
3956
|
+
if source is None or source_stage is None:
|
|
3957
|
+
raise RuntimeError("recombination has no in-memory source wave")
|
|
3958
|
+
if (
|
|
3959
|
+
request.source_portfolio is None
|
|
3960
|
+
or request.source_portfolio.receipt_sha256 != source_stage.receipt_sha256
|
|
3961
|
+
):
|
|
3962
|
+
raise ValueError("recombination source receipt is not the runtime source")
|
|
3963
|
+
assert self.recombination is not None
|
|
3964
|
+
source_archive_utility = self._archive_utilities.get(source_generation)
|
|
3965
|
+
if source_archive_utility is None:
|
|
3966
|
+
raise RuntimeError("recombination lacks the source archive utility cutoff")
|
|
3967
|
+
utility_binder = self.recombination_utility_binder
|
|
3968
|
+
waves_list: list[PortfolioRecombinationWaveRequest] = []
|
|
3969
|
+
for slot, (wave, result) in enumerate(source):
|
|
3970
|
+
scored_source_count = len(result.scored_candidates)
|
|
3971
|
+
source_utilities = (
|
|
3972
|
+
None
|
|
3973
|
+
if utility_binder is None or scored_source_count < 2
|
|
3974
|
+
else utility_binder.bind(
|
|
3975
|
+
source_archive_utility=source_archive_utility,
|
|
3976
|
+
source_wave=wave,
|
|
3977
|
+
source_result=result,
|
|
3978
|
+
)
|
|
3979
|
+
)
|
|
3980
|
+
if (
|
|
3981
|
+
source_utilities is not None
|
|
3982
|
+
and type(source_utilities) is not FrozenArchiveSourceUtilityReceipt
|
|
3983
|
+
):
|
|
3984
|
+
raise TypeError("utility binder returned a foreign receipt")
|
|
3985
|
+
waves_list.append(
|
|
3986
|
+
PortfolioRecombinationWaveRequest(
|
|
3987
|
+
source_wave=wave,
|
|
3988
|
+
source_result=result,
|
|
3989
|
+
ancestor=wave.parent,
|
|
3990
|
+
generation=step.generation,
|
|
3991
|
+
label_prefix=(f"campaign_g{step.generation:02d}_p{slot + 1:02d}"),
|
|
3992
|
+
phase="campaign_portfolio_recombination",
|
|
3993
|
+
source_archive_snapshot=(
|
|
3994
|
+
None if source_utilities is None else source_archive_utility
|
|
3995
|
+
),
|
|
3996
|
+
source_utilities=source_utilities,
|
|
3997
|
+
)
|
|
3998
|
+
)
|
|
3999
|
+
waves = tuple(waves_list)
|
|
4000
|
+
allocation_mode = self.recombination_evaluation_allocation_mode
|
|
4001
|
+
results = (
|
|
4002
|
+
()
|
|
4003
|
+
if allocation_mode
|
|
4004
|
+
is RecombinationEvaluationAllocationMode.RECOURSE_ONLY
|
|
4005
|
+
else await gather_concurrent_stage(
|
|
4006
|
+
self.recombination.run(wave) for wave in waves
|
|
4007
|
+
)
|
|
4008
|
+
)
|
|
4009
|
+
recombination_candidates = tuple(
|
|
4010
|
+
candidate for result in results for candidate in result.candidates
|
|
4011
|
+
)
|
|
4012
|
+
scored_candidate_count = sum(
|
|
4013
|
+
len(result.scored_candidates) for result in results
|
|
4014
|
+
)
|
|
4015
|
+
infeasible_candidate_count = sum(
|
|
4016
|
+
len(result.infeasible_candidates) for result in results
|
|
4017
|
+
)
|
|
4018
|
+
if scored_candidate_count + infeasible_candidate_count != len(
|
|
4019
|
+
recombination_candidates
|
|
4020
|
+
):
|
|
4021
|
+
raise RuntimeError(
|
|
4022
|
+
"recombination disposition partition does not cover selected ITT "
|
|
4023
|
+
"candidates"
|
|
4024
|
+
)
|
|
4025
|
+
planned_capacity = step.planned_candidate_evaluations
|
|
4026
|
+
if len(recombination_candidates) > planned_capacity:
|
|
4027
|
+
raise RuntimeError("recombination exceeded its prepared capacity")
|
|
4028
|
+
recourse_result = None
|
|
4029
|
+
missing_capacity = planned_capacity - len(recombination_candidates)
|
|
4030
|
+
if missing_capacity and self.capacity_recourse is not None:
|
|
4031
|
+
preview = self._preview_archive(recombination_candidates)
|
|
4032
|
+
cache_at_recourse = _cache_misses(
|
|
4033
|
+
await self.composition.engine.evaluation_cache_snapshot()
|
|
4034
|
+
)
|
|
4035
|
+
recourse_state = OptimizerState(
|
|
4036
|
+
generation=step.generation,
|
|
4037
|
+
candidates=tuple((*self._history, *recombination_candidates)),
|
|
4038
|
+
archive=preview.snapshot(),
|
|
4039
|
+
archive_snapshot_hash=pareto_archive_snapshot_hash(
|
|
4040
|
+
preview.snapshot()
|
|
4041
|
+
),
|
|
4042
|
+
unique_evaluations=cache_at_recourse,
|
|
4043
|
+
logical_llm_calls=self._selector_calls,
|
|
4044
|
+
)
|
|
4045
|
+
recourse_request = CampaignCapacityRecourseRequest(
|
|
4046
|
+
campaign_scope_sha256=self.task_sha256,
|
|
4047
|
+
preparation_sha256=request.preparation_sha256,
|
|
4048
|
+
stage_request_sha256=request.request_sha256,
|
|
4049
|
+
generation=step.generation,
|
|
4050
|
+
planned_candidate_occurrences=planned_capacity,
|
|
4051
|
+
realized_candidate_occurrences=len(recombination_candidates),
|
|
4052
|
+
state=recourse_state,
|
|
4053
|
+
)
|
|
4054
|
+
recourse_result = await self.capacity_recourse.fill(recourse_request)
|
|
4055
|
+
validate_campaign_capacity_recourse_result(
|
|
4056
|
+
port=self.capacity_recourse,
|
|
4057
|
+
request=recourse_request,
|
|
4058
|
+
result=recourse_result,
|
|
4059
|
+
)
|
|
4060
|
+
recourse_candidates = (
|
|
4061
|
+
() if recourse_result is None else recourse_result.candidates
|
|
4062
|
+
)
|
|
4063
|
+
candidates = tuple((*recombination_candidates, *recourse_candidates))
|
|
4064
|
+
for candidate in candidates:
|
|
4065
|
+
self._history.append(candidate)
|
|
4066
|
+
self.archive.consider(candidate)
|
|
4067
|
+
self._recombination_results[step.generation] = results
|
|
4068
|
+
self._recombination_source_portfolio_generations[step.generation] = (
|
|
4069
|
+
source_generation
|
|
4070
|
+
)
|
|
4071
|
+
contextual_post_recombination_credit = None
|
|
4072
|
+
contextual_post_stage_survival_credit = None
|
|
4073
|
+
planner = self.contextual_search_planner
|
|
4074
|
+
if planner is not None:
|
|
4075
|
+
source_wave_index = (source_generation + 1) // 2
|
|
4076
|
+
source_observations = tuple(
|
|
4077
|
+
value
|
|
4078
|
+
for value in planner.ledger.observations
|
|
4079
|
+
if value.campaign_scope_sha256 == planner.campaign_scope_sha256
|
|
4080
|
+
and value.wave_index == source_wave_index
|
|
4081
|
+
)
|
|
4082
|
+
if results:
|
|
4083
|
+
contextual_post_recombination_credit = (
|
|
4084
|
+
observe_contextual_post_recombination_credit(
|
|
4085
|
+
campaign_scope_sha256=planner.campaign_scope_sha256,
|
|
4086
|
+
source_wave_index=source_wave_index,
|
|
4087
|
+
observations=source_observations,
|
|
4088
|
+
results=results,
|
|
4089
|
+
post_stage_front_candidate_ids=tuple(
|
|
4090
|
+
sorted(value.candidate_id for value in self.archive.front)
|
|
4091
|
+
),
|
|
4092
|
+
)
|
|
4093
|
+
)
|
|
4094
|
+
delayed_credits = contextual_post_recombination_credit.credits
|
|
4095
|
+
else:
|
|
4096
|
+
contextual_post_stage_survival_credit = (
|
|
4097
|
+
observe_contextual_post_stage_survival(
|
|
4098
|
+
campaign_scope_sha256=planner.campaign_scope_sha256,
|
|
4099
|
+
source_wave_index=source_wave_index,
|
|
4100
|
+
stage_request_sha256=request.request_sha256,
|
|
4101
|
+
observations=source_observations,
|
|
4102
|
+
post_stage_front_candidate_ids=tuple(
|
|
4103
|
+
sorted(value.candidate_id for value in self.archive.front)
|
|
4104
|
+
),
|
|
4105
|
+
)
|
|
4106
|
+
)
|
|
4107
|
+
delayed_credits = contextual_post_stage_survival_credit.credits
|
|
4108
|
+
preview_ledger = type(planner.ledger)(
|
|
4109
|
+
observations=list(planner.ledger.observations),
|
|
4110
|
+
delayed_credits=list(planner.ledger.delayed_credits),
|
|
4111
|
+
allocation_realizations=list(planner.ledger.allocation_realizations),
|
|
4112
|
+
)
|
|
4113
|
+
preview_ledger.append_delayed_credit_batch(delayed_credits)
|
|
4114
|
+
planner.ledger.delayed_credits.extend(delayed_credits)
|
|
4115
|
+
cache_after = _cache_misses(
|
|
4116
|
+
await self.composition.engine.evaluation_cache_snapshot()
|
|
4117
|
+
)
|
|
4118
|
+
return CampaignStageReceipt(
|
|
4119
|
+
request_sha256=request.request_sha256,
|
|
4120
|
+
preparation_sha256=request.preparation_sha256,
|
|
4121
|
+
generation=step.generation,
|
|
4122
|
+
kind=step.kind,
|
|
4123
|
+
candidate_occurrence_count=len(candidates),
|
|
4124
|
+
unique_evaluation_count=cache_after - cache_before,
|
|
4125
|
+
selector_audits=(),
|
|
4126
|
+
result=_object(
|
|
4127
|
+
{
|
|
4128
|
+
"recombination_wave_receipts": [
|
|
4129
|
+
value.receipt.to_record() for value in results
|
|
4130
|
+
],
|
|
4131
|
+
"candidates": [
|
|
4132
|
+
_candidate_record(candidate) for candidate in candidates
|
|
4133
|
+
],
|
|
4134
|
+
"selected_itt_candidate_count": len(recombination_candidates),
|
|
4135
|
+
"scored_candidate_count": scored_candidate_count,
|
|
4136
|
+
"candidate_infeasible_count": infeasible_candidate_count,
|
|
4137
|
+
"candidate_infeasibility_recourse": (
|
|
4138
|
+
"retain_selected_itt_reject_from_archive_no_resampling"
|
|
4139
|
+
),
|
|
4140
|
+
"archive_after": thaw_json(self._archive_record()),
|
|
4141
|
+
"capacity": {
|
|
4142
|
+
"allocation_mode": allocation_mode.value,
|
|
4143
|
+
"native_wave_count": len(waves),
|
|
4144
|
+
"native_wave_evaluation_suppressed": (
|
|
4145
|
+
allocation_mode
|
|
4146
|
+
is RecombinationEvaluationAllocationMode.RECOURSE_ONLY
|
|
4147
|
+
),
|
|
4148
|
+
"planned_candidate_occurrences": planned_capacity,
|
|
4149
|
+
"recombination_candidate_occurrences": len(
|
|
4150
|
+
recombination_candidates
|
|
4151
|
+
),
|
|
4152
|
+
"missing_candidate_occurrences": missing_capacity,
|
|
4153
|
+
"recourse_enabled": self.capacity_recourse is not None,
|
|
4154
|
+
"recourse_candidate_occurrences": len(
|
|
4155
|
+
recourse_candidates
|
|
4156
|
+
),
|
|
4157
|
+
"realized_candidate_occurrences": len(candidates),
|
|
4158
|
+
"capacity_complete": len(candidates) == planned_capacity,
|
|
4159
|
+
"recourse_result": (
|
|
4160
|
+
None
|
|
4161
|
+
if recourse_result is None
|
|
4162
|
+
else recourse_result.to_record()
|
|
4163
|
+
),
|
|
4164
|
+
},
|
|
4165
|
+
"archive_aware_source_utility": utility_binder is not None,
|
|
4166
|
+
"contextual_post_recombination_credit": (
|
|
4167
|
+
None
|
|
4168
|
+
if contextual_post_recombination_credit is None
|
|
4169
|
+
else contextual_post_recombination_credit.to_record()
|
|
4170
|
+
),
|
|
4171
|
+
"contextual_post_stage_survival_credit": (
|
|
4172
|
+
None
|
|
4173
|
+
if contextual_post_stage_survival_credit is None
|
|
4174
|
+
else contextual_post_stage_survival_credit.to_record()
|
|
4175
|
+
),
|
|
4176
|
+
}
|
|
4177
|
+
),
|
|
4178
|
+
)
|
|
4179
|
+
|
|
4180
|
+
async def execute_stage(
|
|
4181
|
+
self,
|
|
4182
|
+
request: CampaignStageRequest,
|
|
4183
|
+
) -> CampaignStageReceipt:
|
|
4184
|
+
self._require_active()
|
|
4185
|
+
if request.preparation_sha256 != self.prepared.preparation_sha256:
|
|
4186
|
+
raise ValueError("stage request names a foreign preparation")
|
|
4187
|
+
step = request.step
|
|
4188
|
+
if step.generation != len(self._stage_receipts) + 1:
|
|
4189
|
+
raise ValueError("stage request is not the next prepared generation")
|
|
4190
|
+
pre_archive_sha256 = typed_json_sha256(self._archive_record())
|
|
4191
|
+
if pre_archive_sha256 != request.archive_utility.archive_sha256:
|
|
4192
|
+
raise ValueError(
|
|
4193
|
+
"stage archive utility differs from the live pre-stage archive"
|
|
4194
|
+
)
|
|
4195
|
+
pending_progress = self._pending_parent_selection_progress
|
|
4196
|
+
if pending_progress is None:
|
|
4197
|
+
if step.generation != 1:
|
|
4198
|
+
raise RuntimeError("campaign parent progress lost its pending stage")
|
|
4199
|
+
else:
|
|
4200
|
+
if pending_progress.generation != step.generation - 1:
|
|
4201
|
+
raise RuntimeError("campaign parent progress is not contiguous")
|
|
4202
|
+
self._parent_selection_progress.append(
|
|
4203
|
+
pending_progress.complete(request.archive_utility)
|
|
4204
|
+
)
|
|
4205
|
+
self._pending_parent_selection_progress = None
|
|
4206
|
+
cache_before = _cache_misses(
|
|
4207
|
+
await self.composition.engine.evaluation_cache_snapshot()
|
|
4208
|
+
)
|
|
4209
|
+
if step.kind is CampaignGenerationKind.PORTFOLIO:
|
|
4210
|
+
receipt = await self._portfolio_stage(request, cache_before=cache_before)
|
|
4211
|
+
else:
|
|
4212
|
+
receipt = await self._recombination_stage(
|
|
4213
|
+
request,
|
|
4214
|
+
cache_before=cache_before,
|
|
4215
|
+
)
|
|
4216
|
+
post_archive_sha256 = typed_json_sha256(self._archive_record())
|
|
4217
|
+
result_record = thaw_json(receipt.result)
|
|
4218
|
+
result_archive_after = freeze_json(result_record.get("archive_after"))
|
|
4219
|
+
if (
|
|
4220
|
+
type(result_archive_after) is not FrozenJsonObject
|
|
4221
|
+
or typed_json_sha256(result_archive_after) != post_archive_sha256
|
|
4222
|
+
):
|
|
4223
|
+
raise RuntimeError(
|
|
4224
|
+
"stage receipt archive-after evidence differs from live state"
|
|
4225
|
+
)
|
|
4226
|
+
pending_progress = _PendingCampaignParentSelectionProgress(
|
|
4227
|
+
generation=step.generation,
|
|
4228
|
+
stage_kind=step.kind,
|
|
4229
|
+
stage_request_sha256=request.request_sha256,
|
|
4230
|
+
stage_receipt_sha256=receipt.receipt_sha256,
|
|
4231
|
+
pre_archive_sha256=pre_archive_sha256,
|
|
4232
|
+
post_archive_sha256=post_archive_sha256,
|
|
4233
|
+
pre_utility=request.archive_utility,
|
|
4234
|
+
)
|
|
4235
|
+
self._archive_utilities[step.generation] = request.archive_utility
|
|
4236
|
+
self._stage_receipts[step.generation] = receipt
|
|
4237
|
+
self._pending_parent_selection_progress = pending_progress
|
|
4238
|
+
return receipt
|
|
4239
|
+
|
|
4240
|
+
def _validate_identifiable_reflection_lineage(
|
|
4241
|
+
self,
|
|
4242
|
+
reflection_input: CampaignIdentifiableReflectionInput,
|
|
4243
|
+
) -> tuple[str, ...]:
|
|
4244
|
+
"""Replay every projected contrast against the exact mutation wave.
|
|
4245
|
+
|
|
4246
|
+
This join happens inside the runtime, before the executor boundary. The
|
|
4247
|
+
executor consequently needs neither the portfolio results nor the later
|
|
4248
|
+
recombination results to obtain durable candidate/operator provenance.
|
|
4249
|
+
"""
|
|
4250
|
+
|
|
4251
|
+
reflection_input.__post_init__()
|
|
4252
|
+
query = reflection_input.query
|
|
4253
|
+
source = self._portfolio_waves.get(query.source_portfolio_generation)
|
|
4254
|
+
if source is None:
|
|
4255
|
+
raise RuntimeError("identifiable reflection has no source portfolio")
|
|
4256
|
+
expected: dict[str, dict[str, object]] = {}
|
|
4257
|
+
for wave, result in source:
|
|
4258
|
+
decision = result.selection_decision
|
|
4259
|
+
if decision is None:
|
|
4260
|
+
raise RuntimeError(
|
|
4261
|
+
"identifiable reflection source omitted its ranked decision"
|
|
4262
|
+
)
|
|
4263
|
+
for selected, member, outcome in zip(
|
|
4264
|
+
decision.members,
|
|
4265
|
+
result.receipt.members,
|
|
4266
|
+
result.outcomes,
|
|
4267
|
+
strict=True,
|
|
4268
|
+
):
|
|
4269
|
+
if member.disposition is not PortfolioMemberDisposition.SCORED:
|
|
4270
|
+
continue
|
|
4271
|
+
child = outcome.candidate
|
|
4272
|
+
if child is None: # Closed by result validation.
|
|
4273
|
+
raise AssertionError("scored mutation source lost its candidate")
|
|
4274
|
+
if member.outcome_sha256 in expected:
|
|
4275
|
+
raise RuntimeError("mutation source repeats outcome evidence")
|
|
4276
|
+
expected[member.outcome_sha256] = {
|
|
4277
|
+
"parent_candidate_id": wave.parent.candidate_id,
|
|
4278
|
+
"child_candidate_id": child.candidate_id,
|
|
4279
|
+
"operator_invocation_id": member.operator_invocation_id,
|
|
4280
|
+
"parent_configuration_sha256": (
|
|
4281
|
+
AuthenticatedHypothesisObservation.configuration_sha256(
|
|
4282
|
+
wave.parent.configuration
|
|
4283
|
+
)
|
|
4284
|
+
),
|
|
4285
|
+
"child_configuration_sha256": (
|
|
4286
|
+
AuthenticatedHypothesisObservation.configuration_sha256(
|
|
4287
|
+
child.configuration
|
|
4288
|
+
)
|
|
4289
|
+
),
|
|
4290
|
+
"option_id": selected.option_id,
|
|
4291
|
+
"option_identity_sha256": selected.option_identity_sha256,
|
|
4292
|
+
"option_family": selected.family,
|
|
4293
|
+
"finite_contract_identity_sha256": (
|
|
4294
|
+
wave.selection_request.finite_variation_contract.identity_sha256
|
|
4295
|
+
),
|
|
4296
|
+
"changed_paths": member.materialization.changed_paths,
|
|
4297
|
+
}
|
|
4298
|
+
observed_source_ids: list[str] = []
|
|
4299
|
+
for contrast in reflection_input.evidence.contrasts:
|
|
4300
|
+
observed_source_ids.append(contrast.source_evidence_id)
|
|
4301
|
+
expected_lineage = expected.get(contrast.source_evidence_id)
|
|
4302
|
+
if expected_lineage is None:
|
|
4303
|
+
raise ValueError(
|
|
4304
|
+
"identifiable contrast is not a scored source mutation"
|
|
4305
|
+
)
|
|
4306
|
+
observed_lineage = {
|
|
4307
|
+
"parent_candidate_id": contrast.parent_candidate_id,
|
|
4308
|
+
"child_candidate_id": contrast.child_candidate_id,
|
|
4309
|
+
"operator_invocation_id": contrast.operator_invocation_id,
|
|
4310
|
+
"parent_configuration_sha256": (contrast.parent_configuration_sha256),
|
|
4311
|
+
"child_configuration_sha256": contrast.child_configuration_sha256,
|
|
4312
|
+
"option_id": contrast.option_id,
|
|
4313
|
+
"option_identity_sha256": contrast.option_identity_sha256,
|
|
4314
|
+
"option_family": contrast.option_family,
|
|
4315
|
+
"finite_contract_identity_sha256": (
|
|
4316
|
+
contrast.finite_contract_identity_sha256
|
|
4317
|
+
),
|
|
4318
|
+
"changed_paths": (contrast.affected_path,),
|
|
4319
|
+
}
|
|
4320
|
+
if observed_lineage != expected_lineage:
|
|
4321
|
+
raise ValueError(
|
|
4322
|
+
"identifiable contrast candidate/operator lineage is foreign"
|
|
4323
|
+
)
|
|
4324
|
+
if contrast.event_index != query.sealed_cutoff_event_index_inclusive:
|
|
4325
|
+
raise ValueError(
|
|
4326
|
+
"identifiable contrast event differs from the source cutoff"
|
|
4327
|
+
)
|
|
4328
|
+
canonical_source_ids = tuple(sorted(observed_source_ids))
|
|
4329
|
+
if len(set(observed_source_ids)) != len(observed_source_ids):
|
|
4330
|
+
raise ValueError("identifiable contrast source evidence must be unique")
|
|
4331
|
+
if self._consumed_reflection_source_evidence_ids.intersection(
|
|
4332
|
+
canonical_source_ids
|
|
4333
|
+
):
|
|
4334
|
+
raise ValueError("identifiable reflection attempted to reuse evidence")
|
|
4335
|
+
return canonical_source_ids
|
|
4336
|
+
|
|
4337
|
+
async def reflect(
|
|
4338
|
+
self,
|
|
4339
|
+
request: CampaignReflectionRequest,
|
|
4340
|
+
) -> CampaignReflectionReceipt:
|
|
4341
|
+
self._require_active()
|
|
4342
|
+
if type(request) is not CampaignReflectionRequest:
|
|
4343
|
+
raise TypeError("request must be an exact CampaignReflectionRequest")
|
|
4344
|
+
CampaignReflectionRequest.__post_init__(request)
|
|
4345
|
+
source_generation = request.wave.source_generation
|
|
4346
|
+
source = self._recombination_results.get(source_generation)
|
|
4347
|
+
if source is None:
|
|
4348
|
+
raise RuntimeError("reflection has no exact recombination source")
|
|
4349
|
+
source_stage = self._stage_receipts.get(source_generation)
|
|
4350
|
+
if (
|
|
4351
|
+
source_stage is None
|
|
4352
|
+
or source_stage.receipt_sha256 != request.source_stage.receipt_sha256
|
|
4353
|
+
):
|
|
4354
|
+
raise ValueError("reflection request differs from the runtime source stage")
|
|
4355
|
+
identifiable_executor = self.identifiable_reflection_executor
|
|
4356
|
+
evidence_source = self.identifiable_reflection_evidence_source
|
|
4357
|
+
reflection_input: CampaignIdentifiableReflectionInput | None = None
|
|
4358
|
+
if identifiable_executor is not None:
|
|
4359
|
+
if evidence_source is None: # Closed by construction; retained defensively.
|
|
4360
|
+
raise RuntimeError(
|
|
4361
|
+
"identifiable reflection omitted its evidence source"
|
|
4362
|
+
)
|
|
4363
|
+
source_portfolio_generation = (
|
|
4364
|
+
self._recombination_source_portfolio_generations.get(source_generation)
|
|
4365
|
+
)
|
|
4366
|
+
if source_portfolio_generation is None:
|
|
4367
|
+
raise RuntimeError(
|
|
4368
|
+
"identifiable reflection lacks its source portfolio cutoff"
|
|
4369
|
+
)
|
|
4370
|
+
query = CampaignIdentifiableReflectionEvidenceQuery(
|
|
4371
|
+
reflection_request_sha256=request.request_sha256,
|
|
4372
|
+
preparation_sha256=request.preparation_sha256,
|
|
4373
|
+
runtime_start_receipt_sha256=(request.runtime_start_receipt_sha256),
|
|
4374
|
+
campaign_sha256=evidence_source.campaign_sha256,
|
|
4375
|
+
workload_instance_sha256=(evidence_source.workload_instance_sha256),
|
|
4376
|
+
evaluator_contract_sha256=(evidence_source.evaluator_contract_sha256),
|
|
4377
|
+
wave=request.wave,
|
|
4378
|
+
source_stage_receipt_sha256=request.source_stage.receipt_sha256,
|
|
4379
|
+
source_portfolio_generation=source_portfolio_generation,
|
|
4380
|
+
prior_cutoff_event_index_exclusive=(
|
|
4381
|
+
self._last_identifiable_reflection_cutoff
|
|
4382
|
+
),
|
|
4383
|
+
sealed_cutoff_event_index_inclusive=source_portfolio_generation,
|
|
4384
|
+
)
|
|
4385
|
+
if (
|
|
4386
|
+
request.request_sha256 in self._identifiable_reflection_inputs
|
|
4387
|
+
or request.request_sha256
|
|
4388
|
+
in self._identifiable_reflection_abstentions
|
|
4389
|
+
):
|
|
4390
|
+
raise ValueError("reflection request was already executed")
|
|
4391
|
+
try:
|
|
4392
|
+
source_projection = evidence_source.project(query)
|
|
4393
|
+
except NoIdentifiableMutationEvidenceError as abstention:
|
|
4394
|
+
# E0 is decided entirely from the sealed committed registry.
|
|
4395
|
+
# Advance the cutoff and authenticate the abstention without
|
|
4396
|
+
# invoking the model executor or campaign-learning lifecycle.
|
|
4397
|
+
record = {
|
|
4398
|
+
**abstention.to_record(),
|
|
4399
|
+
"reflection_request_sha256": request.request_sha256,
|
|
4400
|
+
"query_sha256": query.query_sha256,
|
|
4401
|
+
"source_generation": source_generation,
|
|
4402
|
+
"prior_cutoff_event_index_exclusive": (
|
|
4403
|
+
query.prior_cutoff_event_index_exclusive
|
|
4404
|
+
),
|
|
4405
|
+
"sealed_cutoff_event_index_inclusive": (
|
|
4406
|
+
query.sealed_cutoff_event_index_inclusive
|
|
4407
|
+
),
|
|
4408
|
+
"source_stage_payload_exposed": False,
|
|
4409
|
+
"recombination_results_exposed": False,
|
|
4410
|
+
"learning_registration_permitted": False,
|
|
4411
|
+
"test_admission_permitted": False,
|
|
4412
|
+
}
|
|
4413
|
+
frozen_abstention = _object(record)
|
|
4414
|
+
self._identifiable_reflection_abstentions[
|
|
4415
|
+
request.request_sha256
|
|
4416
|
+
] = frozen_abstention
|
|
4417
|
+
self._last_identifiable_reflection_cutoff = (
|
|
4418
|
+
source_portfolio_generation
|
|
4419
|
+
)
|
|
4420
|
+
return CampaignReflectionReceipt(
|
|
4421
|
+
request_sha256=request.request_sha256,
|
|
4422
|
+
preparation_sha256=request.preparation_sha256,
|
|
4423
|
+
source_generation=source_generation,
|
|
4424
|
+
source_stage_receipt_sha256=(
|
|
4425
|
+
request.source_stage.receipt_sha256
|
|
4426
|
+
),
|
|
4427
|
+
logical_agent_calls=request.wave.call_count,
|
|
4428
|
+
visibility=(
|
|
4429
|
+
ReflectionVisibility.QUARANTINED_UNTIL_BLOCK_CLOSE
|
|
4430
|
+
),
|
|
4431
|
+
status=CampaignReflectionStatus.ABSTAINED,
|
|
4432
|
+
failure_type=None,
|
|
4433
|
+
quarantined_result=frozen_abstention,
|
|
4434
|
+
)
|
|
4435
|
+
if type(source_projection) is not (
|
|
4436
|
+
CampaignIdentifiableReflectionEvidenceProjection
|
|
4437
|
+
):
|
|
4438
|
+
raise TypeError(
|
|
4439
|
+
"identifiable evidence source returned a foreign projection"
|
|
4440
|
+
)
|
|
4441
|
+
reflection_input = CampaignIdentifiableReflectionInput(
|
|
4442
|
+
query=query,
|
|
4443
|
+
source=source_projection,
|
|
4444
|
+
)
|
|
4445
|
+
source_evidence_ids = self._validate_identifiable_reflection_lineage(
|
|
4446
|
+
reflection_input
|
|
4447
|
+
)
|
|
4448
|
+
# Consume before yielding to provider code. A failed or cancelled
|
|
4449
|
+
# call cannot be replayed as though the same cohort were new.
|
|
4450
|
+
self._identifiable_reflection_inputs[request.request_sha256] = (
|
|
4451
|
+
reflection_input
|
|
4452
|
+
)
|
|
4453
|
+
self._consumed_reflection_source_evidence_ids.update(source_evidence_ids)
|
|
4454
|
+
self._last_identifiable_reflection_cutoff = source_portfolio_generation
|
|
4455
|
+
result = await identifiable_executor.reflect(reflection_input)
|
|
4456
|
+
else:
|
|
4457
|
+
legacy_executor = self.legacy_recombination_reflection_executor
|
|
4458
|
+
if legacy_executor is None:
|
|
4459
|
+
legacy_executor = self.reflection_executor
|
|
4460
|
+
if legacy_executor is None:
|
|
4461
|
+
raise RuntimeError(
|
|
4462
|
+
"prepared schedule requires an injected reflection executor"
|
|
4463
|
+
)
|
|
4464
|
+
result = await legacy_executor.reflect(request, source)
|
|
4465
|
+
if type(result) is not FrozenJsonObject or freeze_json(result) is not result:
|
|
4466
|
+
raise TypeError("reflection executor must return a frozen object")
|
|
4467
|
+
if reflection_input is not None:
|
|
4468
|
+
result_record = thaw_json(result)
|
|
4469
|
+
if CAMPAIGN_IDENTIFIABLE_REFLECTION_BINDING_KEY in result_record:
|
|
4470
|
+
raise ValueError(
|
|
4471
|
+
"reflection executor attempted to author the runtime binding"
|
|
4472
|
+
)
|
|
4473
|
+
result_record[CAMPAIGN_IDENTIFIABLE_REFLECTION_BINDING_KEY] = {
|
|
4474
|
+
"schema_version": 1,
|
|
4475
|
+
"input_sha256": reflection_input.input_sha256,
|
|
4476
|
+
"query_sha256": reflection_input.query.query_sha256,
|
|
4477
|
+
"source_projection_sha256": (reflection_input.source.projection_sha256),
|
|
4478
|
+
"registry_snapshot_sha256": (
|
|
4479
|
+
reflection_input.source.registry_snapshot_sha256
|
|
4480
|
+
),
|
|
4481
|
+
"evidence_snapshot_sha256": (reflection_input.evidence.snapshot_sha256),
|
|
4482
|
+
"prior_cutoff_event_index_exclusive": (
|
|
4483
|
+
reflection_input.query.prior_cutoff_event_index_exclusive
|
|
4484
|
+
),
|
|
4485
|
+
"sealed_cutoff_event_index_inclusive": (
|
|
4486
|
+
reflection_input.query.sealed_cutoff_event_index_inclusive
|
|
4487
|
+
),
|
|
4488
|
+
"source_stage_payload_exposed": False,
|
|
4489
|
+
"recombination_results_exposed": False,
|
|
4490
|
+
}
|
|
4491
|
+
result = _object(result_record)
|
|
4492
|
+
receipt = CampaignReflectionReceipt(
|
|
4493
|
+
request_sha256=request.request_sha256,
|
|
4494
|
+
preparation_sha256=request.preparation_sha256,
|
|
4495
|
+
source_generation=source_generation,
|
|
4496
|
+
source_stage_receipt_sha256=request.source_stage.receipt_sha256,
|
|
4497
|
+
logical_agent_calls=request.wave.call_count,
|
|
4498
|
+
visibility=ReflectionVisibility.QUARANTINED_UNTIL_BLOCK_CLOSE,
|
|
4499
|
+
status=CampaignReflectionStatus.COMPLETED,
|
|
4500
|
+
failure_type=None,
|
|
4501
|
+
quarantined_result=result,
|
|
4502
|
+
)
|
|
4503
|
+
learning = self.learning_lifecycle
|
|
4504
|
+
if learning is not None:
|
|
4505
|
+
evidence = learning.reflection_completed(request, receipt, result)
|
|
4506
|
+
if (
|
|
4507
|
+
type(evidence) is not FrozenJsonObject
|
|
4508
|
+
or freeze_json(evidence) is not evidence
|
|
4509
|
+
):
|
|
4510
|
+
raise TypeError("learning reflection hook must return frozen evidence")
|
|
4511
|
+
self._reflection_learning_evidence[receipt.receipt_sha256] = evidence
|
|
4512
|
+
self._reflection_results[receipt.receipt_sha256] = result
|
|
4513
|
+
return receipt
|
|
4514
|
+
|
|
4515
|
+
async def admit_for_testing(
|
|
4516
|
+
self,
|
|
4517
|
+
request: CampaignReflectionTestAdmissionRequest,
|
|
4518
|
+
) -> CampaignReflectionTestAdmissionReceipt:
|
|
4519
|
+
admitted = tuple(sorted(value.receipt_sha256 for value in request.reflections))
|
|
4520
|
+
eligible = tuple(
|
|
4521
|
+
sorted(
|
|
4522
|
+
{
|
|
4523
|
+
*request.previously_test_eligible_reflection_receipt_sha256s,
|
|
4524
|
+
*admitted,
|
|
4525
|
+
}
|
|
4526
|
+
)
|
|
4527
|
+
)
|
|
4528
|
+
if any(value not in self._reflection_results for value in admitted):
|
|
4529
|
+
raise RuntimeError("test admission names unavailable reflection content")
|
|
4530
|
+
learning_evidence: FrozenJsonObject | None = None
|
|
4531
|
+
learning = self.learning_lifecycle
|
|
4532
|
+
if learning is not None:
|
|
4533
|
+
contents = tuple(
|
|
4534
|
+
(
|
|
4535
|
+
reflection,
|
|
4536
|
+
self._reflection_results[reflection.receipt_sha256],
|
|
4537
|
+
)
|
|
4538
|
+
for reflection in sorted(
|
|
4539
|
+
request.reflections,
|
|
4540
|
+
key=lambda value: value.receipt_sha256,
|
|
4541
|
+
)
|
|
4542
|
+
)
|
|
4543
|
+
learning_evidence = learning.reflections_admitted(request, contents)
|
|
4544
|
+
if (
|
|
4545
|
+
type(learning_evidence) is not FrozenJsonObject
|
|
4546
|
+
or freeze_json(learning_evidence) is not learning_evidence
|
|
4547
|
+
):
|
|
4548
|
+
raise TypeError("learning admission hook must return frozen evidence")
|
|
4549
|
+
self._test_eligible_reflection_hashes.update(admitted)
|
|
4550
|
+
return CampaignReflectionTestAdmissionReceipt(
|
|
4551
|
+
request_sha256=request.request_sha256,
|
|
4552
|
+
preparation_sha256=request.preparation_sha256,
|
|
4553
|
+
barrier_generation=request.barrier.generation,
|
|
4554
|
+
admitted_reflection_receipt_sha256s=admitted,
|
|
4555
|
+
test_eligible_reflection_receipt_sha256s=eligible,
|
|
4556
|
+
lifecycle_promoted=False,
|
|
4557
|
+
evidence=_object(
|
|
4558
|
+
{
|
|
4559
|
+
"admission_scope": "controlled_future_testing_only",
|
|
4560
|
+
"normal_retrieval_mutated": False,
|
|
4561
|
+
"learning_lifecycle": (
|
|
4562
|
+
None
|
|
4563
|
+
if learning_evidence is None
|
|
4564
|
+
else thaw_json(learning_evidence)
|
|
4565
|
+
),
|
|
4566
|
+
"reflection_completion_learning": [
|
|
4567
|
+
{
|
|
4568
|
+
"reflection_receipt_sha256": value,
|
|
4569
|
+
"evidence": thaw_json(
|
|
4570
|
+
self._reflection_learning_evidence[value]
|
|
4571
|
+
),
|
|
4572
|
+
}
|
|
4573
|
+
for value in admitted
|
|
4574
|
+
if value in self._reflection_learning_evidence
|
|
4575
|
+
],
|
|
4576
|
+
}
|
|
4577
|
+
),
|
|
4578
|
+
)
|
|
4579
|
+
|
|
4580
|
+
async def finalize(
|
|
4581
|
+
self,
|
|
4582
|
+
request: CampaignFinalizationRequest,
|
|
4583
|
+
) -> CampaignFinalizationReceipt:
|
|
4584
|
+
contextual_terminal_credit = None
|
|
4585
|
+
planner = self.contextual_search_planner
|
|
4586
|
+
if (
|
|
4587
|
+
planner is not None
|
|
4588
|
+
and request.status is CampaignExecutionStatus.COMPLETED
|
|
4589
|
+
and planner.ledger.observations
|
|
4590
|
+
):
|
|
4591
|
+
contextual_terminal_credit = observe_contextual_terminal_persistence(
|
|
4592
|
+
campaign_scope_sha256=planner.campaign_scope_sha256,
|
|
4593
|
+
available_at_wave_index=(
|
|
4594
|
+
len(self.prepared.schedule.portfolio_generations) + 2
|
|
4595
|
+
),
|
|
4596
|
+
finalization_request_sha256=request.request_sha256,
|
|
4597
|
+
observations=tuple(planner.ledger.observations),
|
|
4598
|
+
terminal_front_candidate_ids=tuple(
|
|
4599
|
+
sorted(value.candidate_id for value in self.archive.front)
|
|
4600
|
+
),
|
|
4601
|
+
)
|
|
4602
|
+
preview_ledger = type(planner.ledger)(
|
|
4603
|
+
observations=list(planner.ledger.observations),
|
|
4604
|
+
delayed_credits=list(planner.ledger.delayed_credits),
|
|
4605
|
+
allocation_realizations=list(planner.ledger.allocation_realizations),
|
|
4606
|
+
)
|
|
4607
|
+
preview_ledger.append_delayed_credit_batch(
|
|
4608
|
+
contextual_terminal_credit.credits
|
|
4609
|
+
)
|
|
4610
|
+
planner.ledger.delayed_credits.extend(contextual_terminal_credit.credits)
|
|
4611
|
+
return CampaignFinalizationReceipt(
|
|
4612
|
+
request_sha256=request.request_sha256,
|
|
4613
|
+
preparation_sha256=request.preparation_sha256,
|
|
4614
|
+
status=request.status,
|
|
4615
|
+
evidence=_object(
|
|
4616
|
+
{
|
|
4617
|
+
"stage_count": len(self._stage_receipts),
|
|
4618
|
+
"history_size": len(self._history),
|
|
4619
|
+
"final_archive": thaw_json(self._archive_record()),
|
|
4620
|
+
"parent_selection_progress": [
|
|
4621
|
+
value.to_record() for value in self._parent_selection_progress
|
|
4622
|
+
],
|
|
4623
|
+
"reflection_learning_evidence": [
|
|
4624
|
+
{
|
|
4625
|
+
"reflection_receipt_sha256": receipt_sha256,
|
|
4626
|
+
"evidence": thaw_json(evidence),
|
|
4627
|
+
}
|
|
4628
|
+
for receipt_sha256, evidence in sorted(
|
|
4629
|
+
self._reflection_learning_evidence.items()
|
|
4630
|
+
)
|
|
4631
|
+
],
|
|
4632
|
+
"reflection_evidence_mode": (
|
|
4633
|
+
"identifiable_direct_mutation"
|
|
4634
|
+
if self.identifiable_reflection_executor is not None
|
|
4635
|
+
else (
|
|
4636
|
+
"legacy_recombination"
|
|
4637
|
+
if self.legacy_recombination_reflection_executor is not None
|
|
4638
|
+
or self.reflection_executor is not None
|
|
4639
|
+
else "not_configured"
|
|
4640
|
+
)
|
|
4641
|
+
),
|
|
4642
|
+
"identifiable_reflection_inputs": [
|
|
4643
|
+
value.to_record()
|
|
4644
|
+
for _, value in sorted(
|
|
4645
|
+
self._identifiable_reflection_inputs.items()
|
|
4646
|
+
)
|
|
4647
|
+
],
|
|
4648
|
+
"identifiable_reflection_abstentions": [
|
|
4649
|
+
thaw_json(value)
|
|
4650
|
+
for _, value in sorted(
|
|
4651
|
+
self._identifiable_reflection_abstentions.items()
|
|
4652
|
+
)
|
|
4653
|
+
],
|
|
4654
|
+
"contextual_terminal_persistence_credit": (
|
|
4655
|
+
None
|
|
4656
|
+
if contextual_terminal_credit is None
|
|
4657
|
+
else contextual_terminal_credit.to_record()
|
|
4658
|
+
),
|
|
4659
|
+
}
|
|
4660
|
+
),
|
|
4661
|
+
)
|
|
4662
|
+
|
|
4663
|
+
async def cleanup(
|
|
4664
|
+
self,
|
|
4665
|
+
request: CampaignCleanupRequest,
|
|
4666
|
+
) -> CampaignCleanupReceipt:
|
|
4667
|
+
resource_evidence: FrozenJsonObject
|
|
4668
|
+
if self.owned_resources is None:
|
|
4669
|
+
resource_evidence = _object(
|
|
4670
|
+
{
|
|
4671
|
+
"ownership": "external_to_adapter",
|
|
4672
|
+
"adapter_owned_resource_count": 0,
|
|
4673
|
+
"external_resource_close_required_by_caller": True,
|
|
4674
|
+
}
|
|
4675
|
+
)
|
|
4676
|
+
else:
|
|
4677
|
+
resource_evidence = await self.owned_resources.close()
|
|
4678
|
+
if (
|
|
4679
|
+
type(resource_evidence) is not FrozenJsonObject
|
|
4680
|
+
or freeze_json(resource_evidence) is not resource_evidence
|
|
4681
|
+
):
|
|
4682
|
+
raise TypeError("owned-resource close must return a frozen object")
|
|
4683
|
+
self._cleaned = True
|
|
4684
|
+
return CampaignCleanupReceipt(
|
|
4685
|
+
request_sha256=request.request_sha256,
|
|
4686
|
+
preparation_sha256=request.preparation_sha256,
|
|
4687
|
+
released=True,
|
|
4688
|
+
evidence=_object(
|
|
4689
|
+
{
|
|
4690
|
+
"resource_cleanup": thaw_json(resource_evidence),
|
|
4691
|
+
"runtime_closed": True,
|
|
4692
|
+
}
|
|
4693
|
+
),
|
|
4694
|
+
)
|
|
4695
|
+
|
|
4696
|
+
|
|
4697
|
+
__all__ = [
|
|
4698
|
+
"AgenticPortfolioCampaignRuntime",
|
|
4699
|
+
"ArchiveDiverseEliteCampaignParentSelector",
|
|
4700
|
+
"ArchiveEliteCampaignParentSelector",
|
|
4701
|
+
"ArchiveEliteExplorerCampaignParentSelector",
|
|
4702
|
+
"ArchiveReservoirCampaignParentSelector",
|
|
4703
|
+
"ResidualHypervolumeCampaignParentSelector",
|
|
4704
|
+
"StagnationAwareDiverseCampaignParentSelector",
|
|
4705
|
+
"CAMPAIGN_CONTEXTUAL_HISTORY_KEY",
|
|
4706
|
+
"CAMPAIGN_ARCHIVE_CONTEXT_KEY",
|
|
4707
|
+
"CAMPAIGN_FRONTIER_TARGET_KEY",
|
|
4708
|
+
"CAMPAIGN_IDENTIFIABLE_REFLECTION_BINDING_KEY",
|
|
4709
|
+
"MEMORY_ESTIMAND_STRATUM_SHA256_KEY",
|
|
4710
|
+
"CampaignDecisionSlot",
|
|
4711
|
+
"CampaignIdentifiableReflectionEvidenceProjection",
|
|
4712
|
+
"CampaignIdentifiableReflectionEvidenceQuery",
|
|
4713
|
+
"CampaignIdentifiableReflectionEvidenceSource",
|
|
4714
|
+
"CampaignIdentifiableReflectionInput",
|
|
4715
|
+
"CampaignLearningLifecyclePort",
|
|
4716
|
+
"CampaignOwnedRuntimeResourcePort",
|
|
4717
|
+
"CampaignParentLane",
|
|
4718
|
+
"CampaignParentSelection",
|
|
4719
|
+
"CampaignParentSelectionProgress",
|
|
4720
|
+
"CampaignParentSelectionPort",
|
|
4721
|
+
"CampaignPortfolioContextEnricher",
|
|
4722
|
+
"CampaignPortfolioMemoryEstimandProjection",
|
|
4723
|
+
"CampaignPortfolioMemoryEstimandProjector",
|
|
4724
|
+
"CampaignPortfolioLearningPreparation",
|
|
4725
|
+
"CampaignPortfolioOutcomePreparation",
|
|
4726
|
+
"CampaignPortfolioWaveContext",
|
|
4727
|
+
"CampaignPortfolioWaveBatchFactory",
|
|
4728
|
+
"CampaignPortfolioWaveFactory",
|
|
4729
|
+
"CampaignPortfolioWavePreparationObserver",
|
|
4730
|
+
"CampaignPortfolioWavePreparationReceipt",
|
|
4731
|
+
"CampaignPortfolioOutcomeUpdater",
|
|
4732
|
+
"CampaignRecombinationUtilityBinder",
|
|
4733
|
+
"RecombinationEvaluationAllocationMode",
|
|
4734
|
+
"CampaignLegacyRecombinationReflectionExecutor",
|
|
4735
|
+
"CampaignReflectionFalsificationSource",
|
|
4736
|
+
"CampaignReflectionExecutor",
|
|
4737
|
+
"CampaignSelectorRequestPromptRenderer",
|
|
4738
|
+
"CommittedRegistryIdentifiableReflectionEvidenceSource",
|
|
4739
|
+
]
|