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,938 @@
|
|
|
1
|
+
"""Prospective active-versus-neutral experiments for portfolio memory.
|
|
2
|
+
|
|
3
|
+
The ordinary memory-credit path measures the utility of a complete candidate
|
|
4
|
+
wave. It cannot tell whether the visible memory card, its explicitly supported
|
|
5
|
+
action, or an unrelated slate member created that utility. This module defines
|
|
6
|
+
the narrow workload-neutral successor experiment:
|
|
7
|
+
|
|
8
|
+
* one exact source-bound card is chosen before provider dispatch;
|
|
9
|
+
* two stable lane units are randomized between MEMORY and canonical NEUTRAL;
|
|
10
|
+
* both arms retain the same source identity and required finite candidate-pool
|
|
11
|
+
actions, while the neutral prompt redacts prompt, evidence, score, and action
|
|
12
|
+
compartments; and
|
|
13
|
+
* one realized pair is recorded as experimental evidence, never as an
|
|
14
|
+
identified effect or an online score update.
|
|
15
|
+
|
|
16
|
+
The optimizer may administer a hard supported-action dose in the MEMORY arm.
|
|
17
|
+
If it does, the estimand is the complete memory-guided dose package versus the
|
|
18
|
+
same-pool neutral arm, not prose alone. Workload adapters still own finite
|
|
19
|
+
contract construction and evaluation; no benchmark or provider is imported.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import hashlib
|
|
25
|
+
import json
|
|
26
|
+
import math
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
|
|
29
|
+
from agent_evolve.application.campaign_diagnostic_blocks import (
|
|
30
|
+
CampaignDiagnosticSupportCardInput,
|
|
31
|
+
CampaignDiagnosticSupportLaneInput,
|
|
32
|
+
)
|
|
33
|
+
from agent_evolve.application.portfolio_memory_dose import (
|
|
34
|
+
derive_portfolio_memory_dose_card_support,
|
|
35
|
+
)
|
|
36
|
+
from agent_evolve.application.portfolio_projection import (
|
|
37
|
+
bind_portfolio_experimental_view,
|
|
38
|
+
)
|
|
39
|
+
from agent_evolve.domain.finite_variation import (
|
|
40
|
+
FiniteVariationContract,
|
|
41
|
+
validate_finite_variation_contract,
|
|
42
|
+
)
|
|
43
|
+
from agent_evolve.domain.insight import InsightRef
|
|
44
|
+
from agent_evolve.domain.patch import require_sha256
|
|
45
|
+
from agent_evolve.policies.memory.balanced_subset_blocks import (
|
|
46
|
+
StableMemoryAssignmentUnit,
|
|
47
|
+
)
|
|
48
|
+
from agent_evolve.ports.portfolio_selection import (
|
|
49
|
+
CANONICAL_NEUTRAL_PORTFOLIO_PROMPT_PAYLOAD,
|
|
50
|
+
CANONICAL_REDACTED_PORTFOLIO_EVIDENCE_SHA256,
|
|
51
|
+
PortfolioCard,
|
|
52
|
+
PortfolioCardSourceRegistry,
|
|
53
|
+
PortfolioCardViewTransform,
|
|
54
|
+
PortfolioExperimentalArm,
|
|
55
|
+
PortfolioExperimentalViewReceipt,
|
|
56
|
+
derive_portfolio_card_view,
|
|
57
|
+
)
|
|
58
|
+
from agent_evolve.ports.portfolio_memory_dose import (
|
|
59
|
+
PortfolioMemoryDoseCardSupport,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_ID = (
|
|
64
|
+
"portfolio_memory_randomized_active_neutral_pair"
|
|
65
|
+
)
|
|
66
|
+
PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_VERSION = 1
|
|
67
|
+
PORTFOLIO_MEMORY_MATCHED_CONTROL_DEFINITION_SHA256 = hashlib.sha256(
|
|
68
|
+
b"agent-evolve:portfolio-memory-randomized-active-neutral-pair:v1:"
|
|
69
|
+
b"two-stable-lane-units;external-binary-permutation-rank;"
|
|
70
|
+
b"one-source-bound-card;memory-versus-canonical-redacted-neutral;"
|
|
71
|
+
b"same-required-common-pool-actions;compound-memory-dose-estimand;"
|
|
72
|
+
b"lane-randomized-not-parent-or-full-pool-matched;"
|
|
73
|
+
b"single-block-not-identified;no-online-score-update"
|
|
74
|
+
).hexdigest()
|
|
75
|
+
PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_ID = (
|
|
76
|
+
"portfolio_memory_independent_exact_lane_support"
|
|
77
|
+
)
|
|
78
|
+
PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_VERSION = 1
|
|
79
|
+
PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
80
|
+
b"agent-evolve:portfolio-memory-independent-exact-lane-support:v1:"
|
|
81
|
+
b"one-current-lane;source-bound-cards;exact-finite-action-support;"
|
|
82
|
+
b"task-lane-keyed-card-selection;provider-and-outcome-blind;"
|
|
83
|
+
b"optimization-exposure-only;card-vs-neutral-effect=false;"
|
|
84
|
+
b"online-causal-credit=false"
|
|
85
|
+
).hexdigest()
|
|
86
|
+
_PLAN_DOMAIN = b"agent-evolve:portfolio-memory-matched-control-plan:v1\x00"
|
|
87
|
+
_VIEW_DOMAIN = b"agent-evolve:portfolio-memory-matched-control-view:v1\x00"
|
|
88
|
+
_OUTCOME_DOMAIN = b"agent-evolve:portfolio-memory-matched-control-outcome:v2\x00"
|
|
89
|
+
_SUPPORT_DOMAIN = b"agent-evolve:portfolio-memory-matched-support:v1\x00"
|
|
90
|
+
_CARD_ORDER_DOMAIN = b"agent-evolve:portfolio-memory-matched-card-order:v1\x00"
|
|
91
|
+
_LANE_SUPPORT_DOMAIN = b"agent-evolve:portfolio-memory-lane-support:v1\x00"
|
|
92
|
+
_LANE_CARD_ORDER_DOMAIN = b"agent-evolve:portfolio-memory-lane-card-order:v1\x00"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _canonical_json(value: object) -> bytes:
|
|
96
|
+
return json.dumps(
|
|
97
|
+
value,
|
|
98
|
+
allow_nan=False,
|
|
99
|
+
ensure_ascii=True,
|
|
100
|
+
separators=(",", ":"),
|
|
101
|
+
sort_keys=True,
|
|
102
|
+
).encode("ascii", errors="strict")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
106
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _reference_record(reference: InsightRef) -> dict[str, object]:
|
|
110
|
+
if type(reference) is not InsightRef:
|
|
111
|
+
raise TypeError("reference must be an exact InsightRef")
|
|
112
|
+
InsightRef.__post_init__(reference)
|
|
113
|
+
return {
|
|
114
|
+
"insight_id": reference.insight_id.value,
|
|
115
|
+
"version": reference.version,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass(frozen=True, slots=True)
|
|
120
|
+
class PortfolioMemoryMatchedArmAssignment:
|
|
121
|
+
"""One pre-provider binary arm assignment for a stable lane unit."""
|
|
122
|
+
|
|
123
|
+
unit: StableMemoryAssignmentUnit
|
|
124
|
+
arm: PortfolioExperimentalArm
|
|
125
|
+
schedule_position: int
|
|
126
|
+
assignment_probability_numerator: int = field(init=False, default=1)
|
|
127
|
+
assignment_probability_denominator: int = field(init=False, default=2)
|
|
128
|
+
|
|
129
|
+
def __post_init__(self) -> None:
|
|
130
|
+
if type(self.unit) is not StableMemoryAssignmentUnit:
|
|
131
|
+
raise TypeError("unit must be an exact StableMemoryAssignmentUnit")
|
|
132
|
+
self.unit.__post_init__()
|
|
133
|
+
if self.arm not in (
|
|
134
|
+
PortfolioExperimentalArm.MEMORY,
|
|
135
|
+
PortfolioExperimentalArm.NEUTRAL,
|
|
136
|
+
):
|
|
137
|
+
raise ValueError("matched memory assignments admit only M or N")
|
|
138
|
+
if type(self.schedule_position) is not int or self.schedule_position < 0:
|
|
139
|
+
raise ValueError("schedule_position must be nonnegative")
|
|
140
|
+
|
|
141
|
+
def to_record(self) -> dict[str, object]:
|
|
142
|
+
self.__post_init__()
|
|
143
|
+
return {
|
|
144
|
+
"unit": self.unit.to_record(),
|
|
145
|
+
"arm": self.arm.value,
|
|
146
|
+
"schedule_position": self.schedule_position,
|
|
147
|
+
"assignment_probability": [
|
|
148
|
+
self.assignment_probability_numerator,
|
|
149
|
+
self.assignment_probability_denominator,
|
|
150
|
+
],
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@dataclass(frozen=True, slots=True)
|
|
155
|
+
class PortfolioMemoryMatchedControlPlan:
|
|
156
|
+
"""One authenticated randomized M/N lane pair for an exact card."""
|
|
157
|
+
|
|
158
|
+
reference: InsightRef
|
|
159
|
+
exact_context_sha256: str
|
|
160
|
+
ordered_units: tuple[StableMemoryAssignmentUnit, ...]
|
|
161
|
+
active_unit_rank: int
|
|
162
|
+
assignments: tuple[PortfolioMemoryMatchedArmAssignment, ...]
|
|
163
|
+
policy_id: str = field(
|
|
164
|
+
init=False,
|
|
165
|
+
default=PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_ID,
|
|
166
|
+
)
|
|
167
|
+
policy_version: int = field(
|
|
168
|
+
init=False,
|
|
169
|
+
default=PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_VERSION,
|
|
170
|
+
)
|
|
171
|
+
policy_definition_sha256: str = field(
|
|
172
|
+
init=False,
|
|
173
|
+
default=PORTFOLIO_MEMORY_MATCHED_CONTROL_DEFINITION_SHA256,
|
|
174
|
+
)
|
|
175
|
+
plan_sha256: str = field(init=False)
|
|
176
|
+
|
|
177
|
+
def __post_init__(self) -> None:
|
|
178
|
+
InsightRef.__post_init__(self.reference)
|
|
179
|
+
require_sha256(self.exact_context_sha256, "exact_context_sha256")
|
|
180
|
+
if (
|
|
181
|
+
type(self.ordered_units) is not tuple
|
|
182
|
+
or len(self.ordered_units) != 2
|
|
183
|
+
or any(
|
|
184
|
+
type(value) is not StableMemoryAssignmentUnit
|
|
185
|
+
for value in self.ordered_units
|
|
186
|
+
)
|
|
187
|
+
):
|
|
188
|
+
raise ValueError("a matched block requires exactly two stable units")
|
|
189
|
+
for value in self.ordered_units:
|
|
190
|
+
value.__post_init__()
|
|
191
|
+
if len({value.unit_key for value in self.ordered_units}) != 2:
|
|
192
|
+
raise ValueError("matched units must have distinct identities")
|
|
193
|
+
if len({value.lane_id for value in self.ordered_units}) != 2:
|
|
194
|
+
raise ValueError("matched units must occupy distinct stable lanes")
|
|
195
|
+
if len({value.generation for value in self.ordered_units}) != 1:
|
|
196
|
+
raise ValueError("one matched block cannot span generations")
|
|
197
|
+
if (
|
|
198
|
+
type(self.active_unit_rank) is not int
|
|
199
|
+
or self.active_unit_rank not in (0, 1)
|
|
200
|
+
):
|
|
201
|
+
raise ValueError("active_unit_rank must be one exact binary rank")
|
|
202
|
+
if (
|
|
203
|
+
type(self.assignments) is not tuple
|
|
204
|
+
or len(self.assignments) != 2
|
|
205
|
+
or any(
|
|
206
|
+
type(value) is not PortfolioMemoryMatchedArmAssignment
|
|
207
|
+
for value in self.assignments
|
|
208
|
+
)
|
|
209
|
+
):
|
|
210
|
+
raise ValueError("assignments must cover exactly two units")
|
|
211
|
+
expected = tuple(
|
|
212
|
+
PortfolioMemoryMatchedArmAssignment(
|
|
213
|
+
unit=unit,
|
|
214
|
+
arm=(
|
|
215
|
+
PortfolioExperimentalArm.MEMORY
|
|
216
|
+
if position == self.active_unit_rank
|
|
217
|
+
else PortfolioExperimentalArm.NEUTRAL
|
|
218
|
+
),
|
|
219
|
+
schedule_position=position,
|
|
220
|
+
)
|
|
221
|
+
for position, unit in enumerate(self.ordered_units)
|
|
222
|
+
)
|
|
223
|
+
if self.assignments != expected:
|
|
224
|
+
raise ValueError("assignments do not replay from the external rank")
|
|
225
|
+
if (
|
|
226
|
+
self.policy_id != PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_ID
|
|
227
|
+
or self.policy_version != PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_VERSION
|
|
228
|
+
or self.policy_definition_sha256
|
|
229
|
+
!= PORTFOLIO_MEMORY_MATCHED_CONTROL_DEFINITION_SHA256
|
|
230
|
+
):
|
|
231
|
+
raise ValueError("unsupported matched-control policy")
|
|
232
|
+
object.__setattr__(
|
|
233
|
+
self,
|
|
234
|
+
"plan_sha256",
|
|
235
|
+
_hash(_PLAN_DOMAIN, self._unsigned_record()),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def generation(self) -> int:
|
|
240
|
+
return self.ordered_units[0].generation
|
|
241
|
+
|
|
242
|
+
def assignment_for(
|
|
243
|
+
self,
|
|
244
|
+
*,
|
|
245
|
+
generation: int,
|
|
246
|
+
lane_id: str,
|
|
247
|
+
) -> PortfolioMemoryMatchedArmAssignment:
|
|
248
|
+
self.__post_init__()
|
|
249
|
+
matches = tuple(
|
|
250
|
+
value
|
|
251
|
+
for value in self.assignments
|
|
252
|
+
if value.unit.generation == generation and value.unit.lane_id == lane_id
|
|
253
|
+
)
|
|
254
|
+
if len(matches) != 1:
|
|
255
|
+
raise KeyError("generation/lane does not resolve one matched assignment")
|
|
256
|
+
return matches[0]
|
|
257
|
+
|
|
258
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
259
|
+
return {
|
|
260
|
+
"schema_version": 1,
|
|
261
|
+
"reference": _reference_record(self.reference),
|
|
262
|
+
"exact_context_sha256": self.exact_context_sha256,
|
|
263
|
+
"generation": self.generation,
|
|
264
|
+
"active_unit_rank": self.active_unit_rank,
|
|
265
|
+
"assignments": [value.to_record() for value in self.assignments],
|
|
266
|
+
"policy": {
|
|
267
|
+
"policy_id": self.policy_id,
|
|
268
|
+
"policy_version": self.policy_version,
|
|
269
|
+
"definition_sha256": self.policy_definition_sha256,
|
|
270
|
+
},
|
|
271
|
+
"estimand": (
|
|
272
|
+
"memory_guided_action_dose_package_vs_neutral_with_shared_"
|
|
273
|
+
"required_actions"
|
|
274
|
+
),
|
|
275
|
+
"provider_and_outcome_blind_assignment": True,
|
|
276
|
+
"same_parent_matched": False,
|
|
277
|
+
"full_candidate_pool_matched": False,
|
|
278
|
+
"single_block_card_effect_identified": False,
|
|
279
|
+
"online_score_update_allowed": False,
|
|
280
|
+
"required_analysis": (
|
|
281
|
+
"aggregate_repeated_precommitted_blocks_with_arm_aware_outcomes"
|
|
282
|
+
),
|
|
283
|
+
"required_stronger_successor": (
|
|
284
|
+
"replicated_same_parent_same_full_pool_active_neutral_slots"
|
|
285
|
+
),
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
def to_record(self) -> dict[str, object]:
|
|
289
|
+
self.__post_init__()
|
|
290
|
+
return {**self._unsigned_record(), "plan_sha256": self.plan_sha256}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@dataclass(frozen=True, slots=True)
|
|
294
|
+
class PortfolioMemoryMatchedControlPlanner:
|
|
295
|
+
"""Map one external binary rank to a replayable M/N assignment."""
|
|
296
|
+
|
|
297
|
+
def plan(
|
|
298
|
+
self,
|
|
299
|
+
*,
|
|
300
|
+
reference: InsightRef,
|
|
301
|
+
exact_context_sha256: str,
|
|
302
|
+
ordered_units: tuple[StableMemoryAssignmentUnit, ...],
|
|
303
|
+
active_unit_rank: int,
|
|
304
|
+
) -> PortfolioMemoryMatchedControlPlan:
|
|
305
|
+
assignments = tuple(
|
|
306
|
+
PortfolioMemoryMatchedArmAssignment(
|
|
307
|
+
unit=unit,
|
|
308
|
+
arm=(
|
|
309
|
+
PortfolioExperimentalArm.MEMORY
|
|
310
|
+
if position == active_unit_rank
|
|
311
|
+
else PortfolioExperimentalArm.NEUTRAL
|
|
312
|
+
),
|
|
313
|
+
schedule_position=position,
|
|
314
|
+
)
|
|
315
|
+
for position, unit in enumerate(ordered_units)
|
|
316
|
+
)
|
|
317
|
+
return PortfolioMemoryMatchedControlPlan(
|
|
318
|
+
reference=reference,
|
|
319
|
+
exact_context_sha256=exact_context_sha256,
|
|
320
|
+
ordered_units=ordered_units,
|
|
321
|
+
active_unit_rank=active_unit_rank,
|
|
322
|
+
assignments=assignments,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@dataclass(frozen=True, slots=True)
|
|
327
|
+
class PortfolioMemoryMatchedLaneSupport:
|
|
328
|
+
"""Exact support for the selected source card under one current lane."""
|
|
329
|
+
|
|
330
|
+
lane_id: str
|
|
331
|
+
support: PortfolioMemoryDoseCardSupport
|
|
332
|
+
|
|
333
|
+
def __post_init__(self) -> None:
|
|
334
|
+
if type(self.lane_id) is not str or not self.lane_id:
|
|
335
|
+
raise ValueError("lane_id must be a nonempty string")
|
|
336
|
+
if type(self.support) is not PortfolioMemoryDoseCardSupport:
|
|
337
|
+
raise TypeError("support must be exact PortfolioMemoryDoseCardSupport")
|
|
338
|
+
self.support.__post_init__()
|
|
339
|
+
|
|
340
|
+
def to_record(self) -> dict[str, object]:
|
|
341
|
+
self.__post_init__()
|
|
342
|
+
return {"lane_id": self.lane_id, "support": self.support.to_record()}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
@dataclass(frozen=True, slots=True)
|
|
346
|
+
class PortfolioMemoryMatchedSupportResolution:
|
|
347
|
+
"""Outcome-blind selection of one card supported by every matched lane."""
|
|
348
|
+
|
|
349
|
+
lane_ids: tuple[str, ...]
|
|
350
|
+
eligible_card_keys: tuple[str, ...]
|
|
351
|
+
selected_card_key: str | None
|
|
352
|
+
selected_lane_supports: tuple[PortfolioMemoryMatchedLaneSupport, ...]
|
|
353
|
+
selection_key_sha256: str
|
|
354
|
+
receipt_sha256: str = field(init=False)
|
|
355
|
+
|
|
356
|
+
def __post_init__(self) -> None:
|
|
357
|
+
if (
|
|
358
|
+
type(self.lane_ids) is not tuple
|
|
359
|
+
or len(self.lane_ids) != 2
|
|
360
|
+
or self.lane_ids != tuple(sorted(set(self.lane_ids)))
|
|
361
|
+
):
|
|
362
|
+
raise ValueError("matched support requires two canonical lane IDs")
|
|
363
|
+
if (
|
|
364
|
+
type(self.eligible_card_keys) is not tuple
|
|
365
|
+
or self.eligible_card_keys
|
|
366
|
+
!= tuple(sorted(set(self.eligible_card_keys)))
|
|
367
|
+
):
|
|
368
|
+
raise ValueError("eligible card keys must be unique and canonical")
|
|
369
|
+
require_sha256(self.selection_key_sha256, "selection_key_sha256")
|
|
370
|
+
if self.selected_card_key is None:
|
|
371
|
+
if self.eligible_card_keys or self.selected_lane_supports:
|
|
372
|
+
raise ValueError("ineligible resolution cannot carry selected support")
|
|
373
|
+
else:
|
|
374
|
+
if self.selected_card_key not in self.eligible_card_keys:
|
|
375
|
+
raise ValueError("selected card is outside the eligible set")
|
|
376
|
+
if (
|
|
377
|
+
type(self.selected_lane_supports) is not tuple
|
|
378
|
+
or len(self.selected_lane_supports) != 2
|
|
379
|
+
or tuple(value.lane_id for value in self.selected_lane_supports)
|
|
380
|
+
!= self.lane_ids
|
|
381
|
+
):
|
|
382
|
+
raise ValueError("selected support must cover both lanes exactly")
|
|
383
|
+
for value in self.selected_lane_supports:
|
|
384
|
+
value.__post_init__()
|
|
385
|
+
if value.support.card_key != self.selected_card_key:
|
|
386
|
+
raise ValueError("lane support names another selected card")
|
|
387
|
+
object.__setattr__(
|
|
388
|
+
self,
|
|
389
|
+
"receipt_sha256",
|
|
390
|
+
_hash(_SUPPORT_DOMAIN, self._unsigned_record()),
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
@property
|
|
394
|
+
def eligible(self) -> bool:
|
|
395
|
+
return self.selected_card_key is not None
|
|
396
|
+
|
|
397
|
+
def support_for(self, lane_id: str) -> PortfolioMemoryDoseCardSupport:
|
|
398
|
+
self.__post_init__()
|
|
399
|
+
matches = tuple(
|
|
400
|
+
value.support
|
|
401
|
+
for value in self.selected_lane_supports
|
|
402
|
+
if value.lane_id == lane_id
|
|
403
|
+
)
|
|
404
|
+
if len(matches) != 1:
|
|
405
|
+
raise KeyError("lane lacks selected matched-card support")
|
|
406
|
+
return matches[0]
|
|
407
|
+
|
|
408
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
409
|
+
return {
|
|
410
|
+
"schema_version": 1,
|
|
411
|
+
"eligible": self.eligible,
|
|
412
|
+
"lane_ids": list(self.lane_ids),
|
|
413
|
+
"eligible_card_keys": list(self.eligible_card_keys),
|
|
414
|
+
"selected_card_key": self.selected_card_key,
|
|
415
|
+
"selected_lane_supports": [
|
|
416
|
+
value.to_record() for value in self.selected_lane_supports
|
|
417
|
+
],
|
|
418
|
+
"selection_key_sha256": self.selection_key_sha256,
|
|
419
|
+
"provider_fields_consulted": False,
|
|
420
|
+
"outcome_values_consulted": False,
|
|
421
|
+
"card_vs_neutral_effect_identified": False,
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
def to_record(self) -> dict[str, object]:
|
|
425
|
+
self.__post_init__()
|
|
426
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
@dataclass(frozen=True, slots=True)
|
|
430
|
+
class PortfolioMemoryMatchedSupportResolver:
|
|
431
|
+
"""Choose one complete-support card for a two-lane M/N block."""
|
|
432
|
+
|
|
433
|
+
def resolve(
|
|
434
|
+
self,
|
|
435
|
+
*,
|
|
436
|
+
lanes: tuple[CampaignDiagnosticSupportLaneInput, ...],
|
|
437
|
+
cards: tuple[CampaignDiagnosticSupportCardInput, ...],
|
|
438
|
+
selection_key_sha256: str,
|
|
439
|
+
) -> PortfolioMemoryMatchedSupportResolution:
|
|
440
|
+
if (
|
|
441
|
+
type(lanes) is not tuple
|
|
442
|
+
or len(lanes) != 2
|
|
443
|
+
or any(
|
|
444
|
+
type(value) is not CampaignDiagnosticSupportLaneInput
|
|
445
|
+
for value in lanes
|
|
446
|
+
)
|
|
447
|
+
):
|
|
448
|
+
raise ValueError("matched resolution requires exactly two lane inputs")
|
|
449
|
+
if type(cards) is not tuple or not cards or any(
|
|
450
|
+
type(value) is not CampaignDiagnosticSupportCardInput for value in cards
|
|
451
|
+
):
|
|
452
|
+
raise ValueError("matched resolution requires source card inputs")
|
|
453
|
+
for value in lanes:
|
|
454
|
+
value.__post_init__()
|
|
455
|
+
for value in cards:
|
|
456
|
+
value.__post_init__()
|
|
457
|
+
lane_ids = tuple(sorted(value.lane.lane_id for value in lanes))
|
|
458
|
+
if len(set(lane_ids)) != 2:
|
|
459
|
+
raise ValueError("matched lanes must have distinct identities")
|
|
460
|
+
card_keys = tuple(sorted(value.card.card_key for value in cards))
|
|
461
|
+
if len(set(card_keys)) != len(card_keys):
|
|
462
|
+
raise ValueError("matched source cards must have distinct keys")
|
|
463
|
+
require_sha256(selection_key_sha256, "selection_key_sha256")
|
|
464
|
+
|
|
465
|
+
by_lane = {value.lane.lane_id: value for value in lanes}
|
|
466
|
+
by_card = {value.card.card_key: value for value in cards}
|
|
467
|
+
complete: dict[str, tuple[PortfolioMemoryMatchedLaneSupport, ...]] = {}
|
|
468
|
+
for card_key in card_keys:
|
|
469
|
+
card = by_card[card_key]
|
|
470
|
+
supports: list[PortfolioMemoryMatchedLaneSupport] = []
|
|
471
|
+
for lane_id in lane_ids:
|
|
472
|
+
try:
|
|
473
|
+
support = derive_portfolio_memory_dose_card_support(
|
|
474
|
+
card.semantics,
|
|
475
|
+
by_lane[lane_id].finite_variation_contract,
|
|
476
|
+
)
|
|
477
|
+
except ValueError:
|
|
478
|
+
break
|
|
479
|
+
supports.append(
|
|
480
|
+
PortfolioMemoryMatchedLaneSupport(
|
|
481
|
+
lane_id=lane_id,
|
|
482
|
+
support=support,
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
if len(supports) == len(lane_ids):
|
|
486
|
+
complete[card_key] = tuple(supports)
|
|
487
|
+
|
|
488
|
+
eligible_keys = tuple(sorted(complete))
|
|
489
|
+
if not eligible_keys:
|
|
490
|
+
return PortfolioMemoryMatchedSupportResolution(
|
|
491
|
+
lane_ids=lane_ids,
|
|
492
|
+
eligible_card_keys=(),
|
|
493
|
+
selected_card_key=None,
|
|
494
|
+
selected_lane_supports=(),
|
|
495
|
+
selection_key_sha256=selection_key_sha256,
|
|
496
|
+
)
|
|
497
|
+
key_bytes = bytes.fromhex(selection_key_sha256)
|
|
498
|
+
selected = min(
|
|
499
|
+
eligible_keys,
|
|
500
|
+
key=lambda card_key: (
|
|
501
|
+
hashlib.sha256(
|
|
502
|
+
_CARD_ORDER_DOMAIN
|
|
503
|
+
+ key_bytes
|
|
504
|
+
+ card_key.encode("ascii", errors="strict")
|
|
505
|
+
+ bytes.fromhex(by_card[card_key].card.card_identity_sha256)
|
|
506
|
+
).digest(),
|
|
507
|
+
card_key,
|
|
508
|
+
),
|
|
509
|
+
)
|
|
510
|
+
return PortfolioMemoryMatchedSupportResolution(
|
|
511
|
+
lane_ids=lane_ids,
|
|
512
|
+
eligible_card_keys=eligible_keys,
|
|
513
|
+
selected_card_key=selected,
|
|
514
|
+
selected_lane_supports=complete[selected],
|
|
515
|
+
selection_key_sha256=selection_key_sha256,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
@dataclass(frozen=True, slots=True)
|
|
520
|
+
class PortfolioMemoryLaneSupportResolution:
|
|
521
|
+
"""Outcome-blind card choice for one independently supportable lane.
|
|
522
|
+
|
|
523
|
+
This resolution is an optimization exposure, not an active-versus-neutral
|
|
524
|
+
causal comparison. It exists so an unrelated second parent cannot erase a
|
|
525
|
+
useful, exactly supported memory treatment from the first parent.
|
|
526
|
+
"""
|
|
527
|
+
|
|
528
|
+
lane_id: str
|
|
529
|
+
eligible_card_keys: tuple[str, ...]
|
|
530
|
+
selected_card_key: str | None
|
|
531
|
+
selected_support: PortfolioMemoryDoseCardSupport | None
|
|
532
|
+
selection_key_sha256: str
|
|
533
|
+
receipt_sha256: str = field(init=False)
|
|
534
|
+
|
|
535
|
+
def __post_init__(self) -> None:
|
|
536
|
+
if type(self.lane_id) is not str or not self.lane_id:
|
|
537
|
+
raise ValueError("lane_id must be a nonempty string")
|
|
538
|
+
if (
|
|
539
|
+
type(self.eligible_card_keys) is not tuple
|
|
540
|
+
or self.eligible_card_keys
|
|
541
|
+
!= tuple(sorted(set(self.eligible_card_keys)))
|
|
542
|
+
):
|
|
543
|
+
raise ValueError("eligible card keys must be unique and canonical")
|
|
544
|
+
require_sha256(self.selection_key_sha256, "selection_key_sha256")
|
|
545
|
+
if self.selected_card_key is None:
|
|
546
|
+
if self.eligible_card_keys or self.selected_support is not None:
|
|
547
|
+
raise ValueError("ineligible lane cannot carry selected support")
|
|
548
|
+
else:
|
|
549
|
+
if self.selected_card_key not in self.eligible_card_keys:
|
|
550
|
+
raise ValueError("selected lane card is outside the eligible set")
|
|
551
|
+
if type(self.selected_support) is not PortfolioMemoryDoseCardSupport:
|
|
552
|
+
raise TypeError("eligible lane requires exact selected support")
|
|
553
|
+
self.selected_support.__post_init__()
|
|
554
|
+
if self.selected_support.card_key != self.selected_card_key:
|
|
555
|
+
raise ValueError("selected support names another lane card")
|
|
556
|
+
object.__setattr__(
|
|
557
|
+
self,
|
|
558
|
+
"receipt_sha256",
|
|
559
|
+
_hash(_LANE_SUPPORT_DOMAIN, self._unsigned_record()),
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
@property
|
|
563
|
+
def eligible(self) -> bool:
|
|
564
|
+
return self.selected_card_key is not None
|
|
565
|
+
|
|
566
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
567
|
+
return {
|
|
568
|
+
"schema_version": 1,
|
|
569
|
+
"lane_id": self.lane_id,
|
|
570
|
+
"eligible": self.eligible,
|
|
571
|
+
"eligible_card_keys": list(self.eligible_card_keys),
|
|
572
|
+
"selected_card_key": self.selected_card_key,
|
|
573
|
+
"selected_support": (
|
|
574
|
+
None
|
|
575
|
+
if self.selected_support is None
|
|
576
|
+
else self.selected_support.to_record()
|
|
577
|
+
),
|
|
578
|
+
"selection_key_sha256": self.selection_key_sha256,
|
|
579
|
+
"policy": {
|
|
580
|
+
"policy_id": PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_ID,
|
|
581
|
+
"policy_version": PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_VERSION,
|
|
582
|
+
"definition_sha256": (
|
|
583
|
+
PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_DEFINITION_SHA256
|
|
584
|
+
),
|
|
585
|
+
},
|
|
586
|
+
"provider_fields_consulted": False,
|
|
587
|
+
"outcome_values_consulted": False,
|
|
588
|
+
"card_vs_neutral_effect_identified": False,
|
|
589
|
+
"online_causal_credit_allowed": False,
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
def to_record(self) -> dict[str, object]:
|
|
593
|
+
self.__post_init__()
|
|
594
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
@dataclass(frozen=True, slots=True)
|
|
598
|
+
class PortfolioMemoryLaneSupportResolver:
|
|
599
|
+
"""Select one exactly supported card for one lane without outcome access."""
|
|
600
|
+
|
|
601
|
+
def resolve(
|
|
602
|
+
self,
|
|
603
|
+
*,
|
|
604
|
+
lane: CampaignDiagnosticSupportLaneInput,
|
|
605
|
+
cards: tuple[CampaignDiagnosticSupportCardInput, ...],
|
|
606
|
+
selection_key_sha256: str,
|
|
607
|
+
) -> PortfolioMemoryLaneSupportResolution:
|
|
608
|
+
if type(lane) is not CampaignDiagnosticSupportLaneInput:
|
|
609
|
+
raise TypeError("lane must be an exact diagnostic support lane input")
|
|
610
|
+
lane.__post_init__()
|
|
611
|
+
if type(cards) is not tuple or not cards or any(
|
|
612
|
+
type(value) is not CampaignDiagnosticSupportCardInput for value in cards
|
|
613
|
+
):
|
|
614
|
+
raise ValueError("lane resolution requires source card inputs")
|
|
615
|
+
for value in cards:
|
|
616
|
+
value.__post_init__()
|
|
617
|
+
card_keys = tuple(sorted(value.card.card_key for value in cards))
|
|
618
|
+
if len(set(card_keys)) != len(card_keys):
|
|
619
|
+
raise ValueError("lane source cards must have distinct keys")
|
|
620
|
+
require_sha256(selection_key_sha256, "selection_key_sha256")
|
|
621
|
+
|
|
622
|
+
by_card = {value.card.card_key: value for value in cards}
|
|
623
|
+
supported: dict[str, PortfolioMemoryDoseCardSupport] = {}
|
|
624
|
+
for card_key in card_keys:
|
|
625
|
+
card = by_card[card_key]
|
|
626
|
+
try:
|
|
627
|
+
supported[card_key] = derive_portfolio_memory_dose_card_support(
|
|
628
|
+
card.semantics,
|
|
629
|
+
lane.finite_variation_contract,
|
|
630
|
+
)
|
|
631
|
+
except ValueError:
|
|
632
|
+
continue
|
|
633
|
+
eligible_keys = tuple(sorted(supported))
|
|
634
|
+
if not eligible_keys:
|
|
635
|
+
return PortfolioMemoryLaneSupportResolution(
|
|
636
|
+
lane_id=lane.lane.lane_id,
|
|
637
|
+
eligible_card_keys=(),
|
|
638
|
+
selected_card_key=None,
|
|
639
|
+
selected_support=None,
|
|
640
|
+
selection_key_sha256=selection_key_sha256,
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
key_bytes = bytes.fromhex(selection_key_sha256)
|
|
644
|
+
selected = min(
|
|
645
|
+
eligible_keys,
|
|
646
|
+
key=lambda card_key: (
|
|
647
|
+
hashlib.sha256(
|
|
648
|
+
_LANE_CARD_ORDER_DOMAIN
|
|
649
|
+
+ key_bytes
|
|
650
|
+
+ bytes.fromhex(lane.lane.lane_identity_sha256)
|
|
651
|
+
+ card_key.encode("ascii", errors="strict")
|
|
652
|
+
+ bytes.fromhex(by_card[card_key].card.card_identity_sha256)
|
|
653
|
+
).digest(),
|
|
654
|
+
card_key,
|
|
655
|
+
),
|
|
656
|
+
)
|
|
657
|
+
return PortfolioMemoryLaneSupportResolution(
|
|
658
|
+
lane_id=lane.lane.lane_id,
|
|
659
|
+
eligible_card_keys=eligible_keys,
|
|
660
|
+
selected_card_key=selected,
|
|
661
|
+
selected_support=supported[selected],
|
|
662
|
+
selection_key_sha256=selection_key_sha256,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
@dataclass(frozen=True, slots=True)
|
|
667
|
+
class PortfolioMemoryMatchedArmView:
|
|
668
|
+
"""One materialized M or N view with a shared candidate-pool obligation."""
|
|
669
|
+
|
|
670
|
+
plan_sha256: str
|
|
671
|
+
assignment: PortfolioMemoryMatchedArmAssignment
|
|
672
|
+
cards: tuple[PortfolioCard, ...]
|
|
673
|
+
source_registry: PortfolioCardSourceRegistry
|
|
674
|
+
experimental_view_receipt: PortfolioExperimentalViewReceipt
|
|
675
|
+
required_common_pool_option_ids: tuple[str, ...]
|
|
676
|
+
view_sha256: str = field(init=False)
|
|
677
|
+
|
|
678
|
+
def __post_init__(self) -> None:
|
|
679
|
+
require_sha256(self.plan_sha256, "plan_sha256")
|
|
680
|
+
if type(self.assignment) is not PortfolioMemoryMatchedArmAssignment:
|
|
681
|
+
raise TypeError("assignment must be exact")
|
|
682
|
+
self.assignment.__post_init__()
|
|
683
|
+
if type(self.cards) is not tuple or len(self.cards) != 1:
|
|
684
|
+
raise ValueError("a matched arm view requires exactly one card")
|
|
685
|
+
card = self.cards[0]
|
|
686
|
+
if type(card) is not PortfolioCard:
|
|
687
|
+
raise TypeError("matched arm card must be exact")
|
|
688
|
+
card.__post_init__()
|
|
689
|
+
if type(self.source_registry) is not PortfolioCardSourceRegistry:
|
|
690
|
+
raise TypeError("source_registry must be exact")
|
|
691
|
+
self.source_registry.__post_init__()
|
|
692
|
+
if type(self.experimental_view_receipt) is not PortfolioExperimentalViewReceipt:
|
|
693
|
+
raise TypeError("experimental_view_receipt must be exact")
|
|
694
|
+
self.experimental_view_receipt.__post_init__()
|
|
695
|
+
if self.experimental_view_receipt.arm is not self.assignment.arm:
|
|
696
|
+
raise ValueError("experimental view arm differs from its assignment")
|
|
697
|
+
if (
|
|
698
|
+
type(self.required_common_pool_option_ids) is not tuple
|
|
699
|
+
or not self.required_common_pool_option_ids
|
|
700
|
+
or self.required_common_pool_option_ids
|
|
701
|
+
!= tuple(sorted(set(self.required_common_pool_option_ids)))
|
|
702
|
+
):
|
|
703
|
+
raise ValueError(
|
|
704
|
+
"required common-pool actions must be nonempty and canonical"
|
|
705
|
+
)
|
|
706
|
+
if self.assignment.arm is PortfolioExperimentalArm.MEMORY:
|
|
707
|
+
if card.derived_view_receipt is not None or not card.finite_action_evidence:
|
|
708
|
+
raise ValueError("M requires the pristine evidence-bearing source card")
|
|
709
|
+
else:
|
|
710
|
+
if (
|
|
711
|
+
card.prompt_payload != CANONICAL_NEUTRAL_PORTFOLIO_PROMPT_PAYLOAD
|
|
712
|
+
or card.evidence_sha256
|
|
713
|
+
!= CANONICAL_REDACTED_PORTFOLIO_EVIDENCE_SHA256
|
|
714
|
+
or card.score_components
|
|
715
|
+
or card.assigned_score is not None
|
|
716
|
+
or card.finite_action_evidence
|
|
717
|
+
):
|
|
718
|
+
raise ValueError("N does not use the canonical redacted card view")
|
|
719
|
+
object.__setattr__(
|
|
720
|
+
self,
|
|
721
|
+
"view_sha256",
|
|
722
|
+
_hash(_VIEW_DOMAIN, self._unsigned_record()),
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
@property
|
|
726
|
+
def memory_dose_allowed(self) -> bool:
|
|
727
|
+
return self.assignment.arm is PortfolioExperimentalArm.MEMORY
|
|
728
|
+
|
|
729
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
730
|
+
return {
|
|
731
|
+
"schema_version": 1,
|
|
732
|
+
"plan_sha256": self.plan_sha256,
|
|
733
|
+
"assignment": self.assignment.to_record(),
|
|
734
|
+
"card_snapshot_sha256": (
|
|
735
|
+
self.experimental_view_receipt.card_snapshot_sha256
|
|
736
|
+
),
|
|
737
|
+
"source_registry_sha256": self.source_registry.registry_sha256,
|
|
738
|
+
"experimental_view_receipt_sha256": (
|
|
739
|
+
self.experimental_view_receipt.receipt_sha256
|
|
740
|
+
),
|
|
741
|
+
"required_common_pool_option_ids": list(
|
|
742
|
+
self.required_common_pool_option_ids
|
|
743
|
+
),
|
|
744
|
+
"same_required_common_pool_actions_across_arms": True,
|
|
745
|
+
"memory_dose_allowed": self.memory_dose_allowed,
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
def to_record(self) -> dict[str, object]:
|
|
749
|
+
self.__post_init__()
|
|
750
|
+
return {**self._unsigned_record(), "view_sha256": self.view_sha256}
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def materialize_portfolio_memory_matched_arm(
|
|
754
|
+
*,
|
|
755
|
+
plan: PortfolioMemoryMatchedControlPlan,
|
|
756
|
+
assignment: PortfolioMemoryMatchedArmAssignment,
|
|
757
|
+
source_card: PortfolioCard,
|
|
758
|
+
source_registry: PortfolioCardSourceRegistry,
|
|
759
|
+
finite_variation_contract: FiniteVariationContract,
|
|
760
|
+
) -> PortfolioMemoryMatchedArmView:
|
|
761
|
+
"""Create one source-identical M/N view for a precommitted assignment."""
|
|
762
|
+
|
|
763
|
+
if type(plan) is not PortfolioMemoryMatchedControlPlan:
|
|
764
|
+
raise TypeError("plan must be exact")
|
|
765
|
+
plan.__post_init__()
|
|
766
|
+
if assignment not in plan.assignments:
|
|
767
|
+
raise ValueError("assignment is outside the matched plan")
|
|
768
|
+
if type(source_card) is not PortfolioCard:
|
|
769
|
+
raise TypeError("source_card must be exact")
|
|
770
|
+
source_card.__post_init__()
|
|
771
|
+
if source_card.reference != plan.reference:
|
|
772
|
+
raise ValueError("source card differs from the planned reference")
|
|
773
|
+
if (
|
|
774
|
+
source_card.source_binding is None
|
|
775
|
+
or source_card.derived_view_receipt is not None
|
|
776
|
+
):
|
|
777
|
+
raise ValueError("matched views require a pristine source-bound card")
|
|
778
|
+
if type(source_registry) is not PortfolioCardSourceRegistry:
|
|
779
|
+
raise TypeError("source_registry must be exact")
|
|
780
|
+
source_registry.__post_init__()
|
|
781
|
+
if len(source_registry.source_bindings) != 1 or (
|
|
782
|
+
source_registry.source_bindings[0] != source_card.source_binding
|
|
783
|
+
):
|
|
784
|
+
raise ValueError("matched view registry must contain exactly the source card")
|
|
785
|
+
validate_finite_variation_contract(finite_variation_contract)
|
|
786
|
+
required_ids = tuple(
|
|
787
|
+
sorted(
|
|
788
|
+
{
|
|
789
|
+
value.option_id
|
|
790
|
+
for value in source_card.source_binding.finite_action_evidence
|
|
791
|
+
}
|
|
792
|
+
)
|
|
793
|
+
)
|
|
794
|
+
available_ids = {value.option_id for value in finite_variation_contract.options}
|
|
795
|
+
if not required_ids or not set(required_ids).issubset(available_ids):
|
|
796
|
+
raise ValueError("source card lacks complete support in the current contract")
|
|
797
|
+
|
|
798
|
+
if assignment.arm is PortfolioExperimentalArm.MEMORY:
|
|
799
|
+
cards = (source_card,)
|
|
800
|
+
else:
|
|
801
|
+
transforms = tuple(
|
|
802
|
+
sorted(
|
|
803
|
+
(
|
|
804
|
+
PortfolioCardViewTransform.ACTION_EVIDENCE_REDACTION,
|
|
805
|
+
PortfolioCardViewTransform.EVIDENCE_REDACTION,
|
|
806
|
+
PortfolioCardViewTransform.PROMPT_REDACTION,
|
|
807
|
+
PortfolioCardViewTransform.SCORE_REDACTION,
|
|
808
|
+
),
|
|
809
|
+
key=lambda value: value.value,
|
|
810
|
+
)
|
|
811
|
+
)
|
|
812
|
+
cards = (
|
|
813
|
+
derive_portfolio_card_view(
|
|
814
|
+
source_card,
|
|
815
|
+
prompt_payload=CANONICAL_NEUTRAL_PORTFOLIO_PROMPT_PAYLOAD,
|
|
816
|
+
evidence_sha256=CANONICAL_REDACTED_PORTFOLIO_EVIDENCE_SHA256,
|
|
817
|
+
score_components=(),
|
|
818
|
+
assigned_score=None,
|
|
819
|
+
finite_action_evidence=(),
|
|
820
|
+
transforms=transforms,
|
|
821
|
+
policy_id=PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_ID,
|
|
822
|
+
policy_version=PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_VERSION,
|
|
823
|
+
policy_definition_sha256=(
|
|
824
|
+
PORTFOLIO_MEMORY_MATCHED_CONTROL_DEFINITION_SHA256
|
|
825
|
+
),
|
|
826
|
+
),
|
|
827
|
+
)
|
|
828
|
+
receipt = bind_portfolio_experimental_view(
|
|
829
|
+
arm=assignment.arm,
|
|
830
|
+
cards=cards,
|
|
831
|
+
finite_variation_contract=finite_variation_contract,
|
|
832
|
+
source_registry=source_registry,
|
|
833
|
+
policy_id=PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_ID,
|
|
834
|
+
policy_version=PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_VERSION,
|
|
835
|
+
policy_definition_sha256=(
|
|
836
|
+
PORTFOLIO_MEMORY_MATCHED_CONTROL_DEFINITION_SHA256
|
|
837
|
+
),
|
|
838
|
+
)
|
|
839
|
+
return PortfolioMemoryMatchedArmView(
|
|
840
|
+
plan_sha256=plan.plan_sha256,
|
|
841
|
+
assignment=assignment,
|
|
842
|
+
cards=cards,
|
|
843
|
+
source_registry=source_registry,
|
|
844
|
+
experimental_view_receipt=receipt,
|
|
845
|
+
required_common_pool_option_ids=required_ids,
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
@dataclass(frozen=True, slots=True)
|
|
850
|
+
class PortfolioMemoryMatchedControlOutcome:
|
|
851
|
+
"""Observed M-minus-N contrast from one precommitted lane pair."""
|
|
852
|
+
|
|
853
|
+
plan_sha256: str
|
|
854
|
+
generation: int
|
|
855
|
+
reference: InsightRef
|
|
856
|
+
aggregation_binding_sha256: str
|
|
857
|
+
active_view_sha256: str
|
|
858
|
+
neutral_view_sha256: str
|
|
859
|
+
active_result_receipt_sha256: str
|
|
860
|
+
neutral_result_receipt_sha256: str
|
|
861
|
+
active_wave_reward: float
|
|
862
|
+
neutral_wave_reward: float
|
|
863
|
+
outcome_sha256: str = field(init=False)
|
|
864
|
+
|
|
865
|
+
def __post_init__(self) -> None:
|
|
866
|
+
for name in (
|
|
867
|
+
"plan_sha256",
|
|
868
|
+
"aggregation_binding_sha256",
|
|
869
|
+
"active_view_sha256",
|
|
870
|
+
"neutral_view_sha256",
|
|
871
|
+
"active_result_receipt_sha256",
|
|
872
|
+
"neutral_result_receipt_sha256",
|
|
873
|
+
):
|
|
874
|
+
require_sha256(getattr(self, name), name)
|
|
875
|
+
if type(self.generation) is not int or self.generation <= 0:
|
|
876
|
+
raise ValueError("generation must be a positive exact integer")
|
|
877
|
+
if type(self.reference) is not InsightRef:
|
|
878
|
+
raise TypeError("reference must be an exact InsightRef")
|
|
879
|
+
InsightRef.__post_init__(self.reference)
|
|
880
|
+
for name in ("active_wave_reward", "neutral_wave_reward"):
|
|
881
|
+
value = getattr(self, name)
|
|
882
|
+
if type(value) is not float or not math.isfinite(value):
|
|
883
|
+
raise TypeError(f"{name} must be a finite canonical float")
|
|
884
|
+
object.__setattr__(
|
|
885
|
+
self,
|
|
886
|
+
"outcome_sha256",
|
|
887
|
+
_hash(_OUTCOME_DOMAIN, self._unsigned_record()),
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
@property
|
|
891
|
+
def observed_active_minus_neutral(self) -> float:
|
|
892
|
+
return self.active_wave_reward - self.neutral_wave_reward
|
|
893
|
+
|
|
894
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
895
|
+
return {
|
|
896
|
+
"schema_version": 2,
|
|
897
|
+
"plan_sha256": self.plan_sha256,
|
|
898
|
+
"generation": self.generation,
|
|
899
|
+
"reference": _reference_record(self.reference),
|
|
900
|
+
"aggregation_binding_sha256": self.aggregation_binding_sha256,
|
|
901
|
+
"active_view_sha256": self.active_view_sha256,
|
|
902
|
+
"neutral_view_sha256": self.neutral_view_sha256,
|
|
903
|
+
"active_result_receipt_sha256": self.active_result_receipt_sha256,
|
|
904
|
+
"neutral_result_receipt_sha256": self.neutral_result_receipt_sha256,
|
|
905
|
+
"active_wave_reward_hex": self.active_wave_reward.hex(),
|
|
906
|
+
"neutral_wave_reward_hex": self.neutral_wave_reward.hex(),
|
|
907
|
+
"observed_active_minus_neutral_hex": (
|
|
908
|
+
self.observed_active_minus_neutral.hex()
|
|
909
|
+
),
|
|
910
|
+
"single_block_card_effect_identified": False,
|
|
911
|
+
"online_score_update_allowed": False,
|
|
912
|
+
"analysis_scope": "append_only_arm_aware_experimental_observation",
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
def to_record(self) -> dict[str, object]:
|
|
916
|
+
self.__post_init__()
|
|
917
|
+
return {**self._unsigned_record(), "outcome_sha256": self.outcome_sha256}
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
__all__ = [
|
|
921
|
+
"PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_DEFINITION_SHA256",
|
|
922
|
+
"PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_ID",
|
|
923
|
+
"PORTFOLIO_MEMORY_LANE_SUPPORT_POLICY_VERSION",
|
|
924
|
+
"PORTFOLIO_MEMORY_MATCHED_CONTROL_DEFINITION_SHA256",
|
|
925
|
+
"PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_ID",
|
|
926
|
+
"PORTFOLIO_MEMORY_MATCHED_CONTROL_POLICY_VERSION",
|
|
927
|
+
"PortfolioMemoryMatchedArmAssignment",
|
|
928
|
+
"PortfolioMemoryMatchedArmView",
|
|
929
|
+
"PortfolioMemoryMatchedControlOutcome",
|
|
930
|
+
"PortfolioMemoryMatchedControlPlan",
|
|
931
|
+
"PortfolioMemoryMatchedControlPlanner",
|
|
932
|
+
"PortfolioMemoryMatchedLaneSupport",
|
|
933
|
+
"PortfolioMemoryMatchedSupportResolution",
|
|
934
|
+
"PortfolioMemoryMatchedSupportResolver",
|
|
935
|
+
"PortfolioMemoryLaneSupportResolution",
|
|
936
|
+
"PortfolioMemoryLaneSupportResolver",
|
|
937
|
+
"materialize_portfolio_memory_matched_arm",
|
|
938
|
+
]
|