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,930 @@
|
|
|
1
|
+
"""Workload-neutral complete blocks for diagnostic insight exposure.
|
|
2
|
+
|
|
3
|
+
A campaign may admit one or more quarantined cards after reflection, while its
|
|
4
|
+
portfolio stage exposes a fixed number of independently assigned lanes. This
|
|
5
|
+
module fills any unused lanes with preregistered control cards and constructs a
|
|
6
|
+
complete singleton-subset block: each eligible card is shown exactly once and
|
|
7
|
+
withheld from every other lane. Consequently every card has treated and
|
|
8
|
+
control support without depending on provider completion order or outcomes.
|
|
9
|
+
|
|
10
|
+
The caller owns cohort selection, lifecycle admission, stable lane identities,
|
|
11
|
+
and the externally generated permutation rank. The planner owns only the
|
|
12
|
+
portable experimental design.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import math
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from enum import Enum
|
|
22
|
+
|
|
23
|
+
from agent_evolve.application.portfolio_memory_dose import (
|
|
24
|
+
PortfolioMemoryDoseCardSemantics,
|
|
25
|
+
derive_portfolio_memory_dose_card_support,
|
|
26
|
+
)
|
|
27
|
+
from agent_evolve.domain.finite_variation import (
|
|
28
|
+
FiniteVariationContract,
|
|
29
|
+
validate_finite_variation_contract,
|
|
30
|
+
)
|
|
31
|
+
from agent_evolve.domain.insight import InsightRef
|
|
32
|
+
from agent_evolve.domain.patch import require_sha256
|
|
33
|
+
from agent_evolve.policies.memory.balanced_subset_blocks import (
|
|
34
|
+
BalancedSubsetBlockPlan,
|
|
35
|
+
BalancedSubsetBlockPlanner,
|
|
36
|
+
StableMemoryAssignmentUnit,
|
|
37
|
+
)
|
|
38
|
+
from agent_evolve.policies.memory.compatibility_matching import (
|
|
39
|
+
CanonicalLaneCardMatchingPlanner,
|
|
40
|
+
LaneCardCompatibility,
|
|
41
|
+
LaneCardMatchingCard,
|
|
42
|
+
LaneCardMatchingInput,
|
|
43
|
+
LaneCardMatchingLane,
|
|
44
|
+
LaneCardMatchingReceipt,
|
|
45
|
+
)
|
|
46
|
+
from agent_evolve.policies.memory.staged_causal import CausalSearchScorePolicy
|
|
47
|
+
from agent_evolve.ports.portfolio_memory_dose import (
|
|
48
|
+
PortfolioMemoryDoseCardSupport,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_ID = (
|
|
53
|
+
"campaign_diagnostic_singleton_complete_block"
|
|
54
|
+
)
|
|
55
|
+
CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_VERSION = 1
|
|
56
|
+
CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
57
|
+
b"agent-evolve:campaign-diagnostic-singleton-complete-block:v1:"
|
|
58
|
+
b"active-quarantine-plus-preregistered-controls:one-treatment-per-lane:"
|
|
59
|
+
b"complete-balanced-subset-block:external-permutation-rank"
|
|
60
|
+
).hexdigest()
|
|
61
|
+
|
|
62
|
+
CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_ID = (
|
|
63
|
+
"campaign_diagnostic_complete_bipartite_support_audit"
|
|
64
|
+
)
|
|
65
|
+
CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_VERSION = 2
|
|
66
|
+
CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
67
|
+
b"agent-evolve:campaign-diagnostic-complete-bipartite-support-audit:v2:"
|
|
68
|
+
b"prospective-positive-lane-card-graph:equal-card-and-lane-cardinality:"
|
|
69
|
+
b"minimum-two-units:every-card-compatible-with-every-lane:"
|
|
70
|
+
b"eligible-for-balanced-randomized-singleton-assignment-or-fail-closed:"
|
|
71
|
+
b"assignment-audit-is-not-a-card-vs-neutral-causal-effect"
|
|
72
|
+
).hexdigest()
|
|
73
|
+
CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_ID = (
|
|
74
|
+
"campaign_diagnostic_complete_support_cohort_selection"
|
|
75
|
+
)
|
|
76
|
+
CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_VERSION = 1
|
|
77
|
+
CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
78
|
+
b"agent-evolve:campaign-diagnostic-complete-support-cohort-selection:v1:"
|
|
79
|
+
b"prospective-positive-lane-card-graph:card-supported-by-every-lane:"
|
|
80
|
+
b"externally-keyed-card-order:select-exact-lane-count:"
|
|
81
|
+
b"provider-outcome-and-workload-neutral:fail-closed"
|
|
82
|
+
).hexdigest()
|
|
83
|
+
_COMPATIBILITY_AUDIT_DOMAIN = (
|
|
84
|
+
b"agent-evolve:campaign-diagnostic-compatibility-audit:v1\x00"
|
|
85
|
+
)
|
|
86
|
+
_COHORT_SELECTION_DOMAIN = (
|
|
87
|
+
b"agent-evolve:campaign-diagnostic-cohort-selection:v1\x00"
|
|
88
|
+
)
|
|
89
|
+
_COHORT_CARD_ORDER_DOMAIN = (
|
|
90
|
+
b"agent-evolve:campaign-diagnostic-cohort-card-order:v1\x00"
|
|
91
|
+
)
|
|
92
|
+
CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_ID = (
|
|
93
|
+
"campaign_diagnostic_complete_support_resolution"
|
|
94
|
+
)
|
|
95
|
+
CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_VERSION = 1
|
|
96
|
+
CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
97
|
+
b"agent-evolve:campaign-diagnostic-complete-support-resolution:v1:"
|
|
98
|
+
b"typed-current-finite-contract-per-stable-lane:typed-card-semantics:"
|
|
99
|
+
b"prospective-positive-support-derivation:outcome-blind-complete-support-"
|
|
100
|
+
b"cohort-selection:canonical-matching:complete-bipartite-audit:fail-closed"
|
|
101
|
+
).hexdigest()
|
|
102
|
+
_SUPPORT_RESOLUTION_DOMAIN = (
|
|
103
|
+
b"agent-evolve:campaign-diagnostic-support-resolution:v1\x00"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _canonical_json(value: object) -> bytes:
|
|
108
|
+
return json.dumps(
|
|
109
|
+
value,
|
|
110
|
+
allow_nan=False,
|
|
111
|
+
ensure_ascii=True,
|
|
112
|
+
separators=(",", ":"),
|
|
113
|
+
sort_keys=True,
|
|
114
|
+
).encode("ascii", errors="strict")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class CampaignDiagnosticCompatibilityStatus(str, Enum):
|
|
118
|
+
"""Whether the positive support graph admits the declared randomization."""
|
|
119
|
+
|
|
120
|
+
ELIGIBLE = "eligible_complete_bipartite_support"
|
|
121
|
+
INELIGIBLE = "ineligible_fail_closed_no_causal_credit"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class CampaignDiagnosticCohortSelectionStatus(str, Enum):
|
|
125
|
+
"""Whether enough universally supported cards form one complete block."""
|
|
126
|
+
|
|
127
|
+
ELIGIBLE = "eligible_complete_support_cohort_selected"
|
|
128
|
+
INELIGIBLE = "ineligible_insufficient_complete_support_cards"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass(frozen=True, slots=True)
|
|
132
|
+
class CampaignDiagnosticSupportLaneInput:
|
|
133
|
+
"""One stable lane and its current, parent-bound finite action contract."""
|
|
134
|
+
|
|
135
|
+
lane: LaneCardMatchingLane
|
|
136
|
+
finite_variation_contract: FiniteVariationContract
|
|
137
|
+
|
|
138
|
+
def __post_init__(self) -> None:
|
|
139
|
+
if type(self.lane) is not LaneCardMatchingLane:
|
|
140
|
+
raise TypeError("lane must be an exact LaneCardMatchingLane")
|
|
141
|
+
self.lane.__post_init__()
|
|
142
|
+
if type(self.finite_variation_contract) is not FiniteVariationContract:
|
|
143
|
+
raise TypeError(
|
|
144
|
+
"finite_variation_contract must be an exact FiniteVariationContract"
|
|
145
|
+
)
|
|
146
|
+
validate_finite_variation_contract(self.finite_variation_contract)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass(frozen=True, slots=True)
|
|
150
|
+
class CampaignDiagnosticSupportCardInput:
|
|
151
|
+
"""One source-bound card and its trusted executable action semantics."""
|
|
152
|
+
|
|
153
|
+
card: LaneCardMatchingCard
|
|
154
|
+
semantics: PortfolioMemoryDoseCardSemantics
|
|
155
|
+
|
|
156
|
+
def __post_init__(self) -> None:
|
|
157
|
+
if type(self.card) is not LaneCardMatchingCard:
|
|
158
|
+
raise TypeError("card must be an exact LaneCardMatchingCard")
|
|
159
|
+
self.card.__post_init__()
|
|
160
|
+
if type(self.semantics) is not PortfolioMemoryDoseCardSemantics:
|
|
161
|
+
raise TypeError(
|
|
162
|
+
"semantics must be exact PortfolioMemoryDoseCardSemantics"
|
|
163
|
+
)
|
|
164
|
+
self.semantics.__post_init__()
|
|
165
|
+
if self.card.card_key != self.semantics.card_key:
|
|
166
|
+
raise ValueError("matching card and executable semantics differ")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass(frozen=True, slots=True)
|
|
170
|
+
class CampaignDiagnosticDerivedSupportEdge:
|
|
171
|
+
"""One positive lane/card edge and its exact finite-option support."""
|
|
172
|
+
|
|
173
|
+
lane_id: str
|
|
174
|
+
card_key: str
|
|
175
|
+
support: PortfolioMemoryDoseCardSupport
|
|
176
|
+
|
|
177
|
+
def __post_init__(self) -> None:
|
|
178
|
+
if type(self.support) is not PortfolioMemoryDoseCardSupport:
|
|
179
|
+
raise TypeError("support must be exact PortfolioMemoryDoseCardSupport")
|
|
180
|
+
self.support.__post_init__()
|
|
181
|
+
if self.support.card_key != self.card_key:
|
|
182
|
+
raise ValueError("support edge and support card key differ")
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def evidence_sha256(self) -> str:
|
|
186
|
+
return hashlib.sha256(
|
|
187
|
+
_canonical_json(self.support.to_record())
|
|
188
|
+
).hexdigest()
|
|
189
|
+
|
|
190
|
+
def to_record(self) -> dict[str, object]:
|
|
191
|
+
self.__post_init__()
|
|
192
|
+
return {
|
|
193
|
+
"lane_id": self.lane_id,
|
|
194
|
+
"card_key": self.card_key,
|
|
195
|
+
"compatibility_evidence_sha256": self.evidence_sha256,
|
|
196
|
+
"support": self.support.to_record(),
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass(frozen=True, slots=True)
|
|
201
|
+
class CampaignDiagnosticRejectedSupportEdge:
|
|
202
|
+
"""One prospectively incompatible lane/card pair, without raw error text."""
|
|
203
|
+
|
|
204
|
+
lane_id: str
|
|
205
|
+
card_key: str
|
|
206
|
+
reason_sha256: str
|
|
207
|
+
|
|
208
|
+
def __post_init__(self) -> None:
|
|
209
|
+
require_sha256(self.reason_sha256, "reason_sha256")
|
|
210
|
+
|
|
211
|
+
def to_record(self) -> dict[str, str]:
|
|
212
|
+
self.__post_init__()
|
|
213
|
+
return {
|
|
214
|
+
"lane_id": self.lane_id,
|
|
215
|
+
"card_key": self.card_key,
|
|
216
|
+
"status": "incompatible",
|
|
217
|
+
"reason_sha256": self.reason_sha256,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@dataclass(frozen=True, slots=True)
|
|
222
|
+
class CampaignDiagnosticCompleteSupportCohortSelection:
|
|
223
|
+
"""Select a randomized, replayable complete-support card cohort.
|
|
224
|
+
|
|
225
|
+
Reflection may yield more candidate insights than a diagnostic wave has
|
|
226
|
+
stable lanes. Compatibility is necessarily parent-local, so selecting
|
|
227
|
+
the experimental cohort before those parents exist either broadens action
|
|
228
|
+
semantics or creates positivity violations. This policy filters only on
|
|
229
|
+
the prospective positive compatibility graph, then uses an external key to
|
|
230
|
+
sample exactly one lane-sized cohort before provider dispatch.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
matching_input: LaneCardMatchingInput
|
|
234
|
+
cohort_size: int
|
|
235
|
+
selection_key_sha256: str
|
|
236
|
+
full_support_card_keys: tuple[str, ...] = field(init=False)
|
|
237
|
+
selected_card_keys: tuple[str, ...] = field(init=False)
|
|
238
|
+
selected_matching_input: LaneCardMatchingInput | None = field(init=False)
|
|
239
|
+
status: CampaignDiagnosticCohortSelectionStatus = field(init=False)
|
|
240
|
+
receipt_sha256: str = field(init=False)
|
|
241
|
+
policy_id: str = field(
|
|
242
|
+
init=False,
|
|
243
|
+
default=CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_ID,
|
|
244
|
+
)
|
|
245
|
+
policy_version: int = field(
|
|
246
|
+
init=False,
|
|
247
|
+
default=CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_VERSION,
|
|
248
|
+
)
|
|
249
|
+
policy_definition_sha256: str = field(
|
|
250
|
+
init=False,
|
|
251
|
+
default=CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_DEFINITION_SHA256,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def __post_init__(self) -> None:
|
|
255
|
+
if type(self.matching_input) is not LaneCardMatchingInput:
|
|
256
|
+
raise TypeError("matching_input must be exact LaneCardMatchingInput")
|
|
257
|
+
self.matching_input.__post_init__()
|
|
258
|
+
if type(self.cohort_size) is not int or self.cohort_size < 2:
|
|
259
|
+
raise ValueError("cohort_size must be an exact integer of at least two")
|
|
260
|
+
if self.cohort_size != len(self.matching_input.lanes):
|
|
261
|
+
raise ValueError("cohort_size must exactly fill the stable lanes")
|
|
262
|
+
require_sha256(self.selection_key_sha256, "selection_key_sha256")
|
|
263
|
+
positive = {
|
|
264
|
+
(value.lane_id, value.card_key)
|
|
265
|
+
for value in self.matching_input.compatibilities
|
|
266
|
+
}
|
|
267
|
+
lane_ids = tuple(value.lane_id for value in self.matching_input.lanes)
|
|
268
|
+
full_support = tuple(
|
|
269
|
+
card.card_key
|
|
270
|
+
for card in self.matching_input.cards
|
|
271
|
+
if all((lane_id, card.card_key) in positive for lane_id in lane_ids)
|
|
272
|
+
)
|
|
273
|
+
object.__setattr__(self, "full_support_card_keys", full_support)
|
|
274
|
+
eligible = len(full_support) >= self.cohort_size
|
|
275
|
+
if eligible:
|
|
276
|
+
cards_by_key = {
|
|
277
|
+
value.card_key: value for value in self.matching_input.cards
|
|
278
|
+
}
|
|
279
|
+
ordered = tuple(
|
|
280
|
+
sorted(
|
|
281
|
+
full_support,
|
|
282
|
+
key=lambda card_key: (
|
|
283
|
+
hashlib.sha256(
|
|
284
|
+
_COHORT_CARD_ORDER_DOMAIN
|
|
285
|
+
+ bytes.fromhex(self.selection_key_sha256)
|
|
286
|
+
+ card_key.encode("ascii", errors="strict")
|
|
287
|
+
+ bytes.fromhex(
|
|
288
|
+
cards_by_key[card_key].card_identity_sha256
|
|
289
|
+
)
|
|
290
|
+
).digest(),
|
|
291
|
+
card_key,
|
|
292
|
+
),
|
|
293
|
+
)
|
|
294
|
+
)
|
|
295
|
+
selected_set = frozenset(ordered[: self.cohort_size])
|
|
296
|
+
selected_keys = tuple(sorted(selected_set))
|
|
297
|
+
selected_input = LaneCardMatchingInput(
|
|
298
|
+
lanes=self.matching_input.lanes,
|
|
299
|
+
cards=tuple(
|
|
300
|
+
value
|
|
301
|
+
for value in self.matching_input.cards
|
|
302
|
+
if value.card_key in selected_set
|
|
303
|
+
),
|
|
304
|
+
compatibilities=tuple(
|
|
305
|
+
value
|
|
306
|
+
for value in self.matching_input.compatibilities
|
|
307
|
+
if value.card_key in selected_set
|
|
308
|
+
),
|
|
309
|
+
)
|
|
310
|
+
status = CampaignDiagnosticCohortSelectionStatus.ELIGIBLE
|
|
311
|
+
else:
|
|
312
|
+
selected_keys = ()
|
|
313
|
+
selected_input = None
|
|
314
|
+
status = CampaignDiagnosticCohortSelectionStatus.INELIGIBLE
|
|
315
|
+
object.__setattr__(self, "selected_card_keys", selected_keys)
|
|
316
|
+
object.__setattr__(self, "selected_matching_input", selected_input)
|
|
317
|
+
object.__setattr__(self, "status", status)
|
|
318
|
+
object.__setattr__(
|
|
319
|
+
self,
|
|
320
|
+
"receipt_sha256",
|
|
321
|
+
hashlib.sha256(
|
|
322
|
+
_COHORT_SELECTION_DOMAIN + _canonical_json(self._unsigned_record())
|
|
323
|
+
).hexdigest(),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def eligible(self) -> bool:
|
|
328
|
+
return self.status is CampaignDiagnosticCohortSelectionStatus.ELIGIBLE
|
|
329
|
+
|
|
330
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
331
|
+
return {
|
|
332
|
+
"schema_version": 1,
|
|
333
|
+
"policy": {
|
|
334
|
+
"policy_id": self.policy_id,
|
|
335
|
+
"policy_version": self.policy_version,
|
|
336
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
337
|
+
},
|
|
338
|
+
"matching_input_sha256": self.matching_input.input_sha256,
|
|
339
|
+
"compatibility_graph_sha256": (
|
|
340
|
+
self.matching_input.compatibility_graph_sha256
|
|
341
|
+
),
|
|
342
|
+
"cohort_size": self.cohort_size,
|
|
343
|
+
"selection_key_sha256": self.selection_key_sha256,
|
|
344
|
+
"full_support_card_keys": list(self.full_support_card_keys),
|
|
345
|
+
"selected_card_keys": list(self.selected_card_keys),
|
|
346
|
+
"status": self.status.value,
|
|
347
|
+
"provider_fields_consulted": False,
|
|
348
|
+
"outcome_values_consulted": False,
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
def to_record(self) -> dict[str, object]:
|
|
352
|
+
self.__post_init__()
|
|
353
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@dataclass(frozen=True, slots=True)
|
|
357
|
+
class CampaignDiagnosticCompatibilityAudit:
|
|
358
|
+
"""Authenticate complete support before randomized card assignment.
|
|
359
|
+
|
|
360
|
+
A maximum matching is enough to administer one compatible card per lane,
|
|
361
|
+
but it is not enough to randomize every card over every lane. This audit
|
|
362
|
+
closes that distinction. The existing balanced singleton law may be used
|
|
363
|
+
only when lane/card cardinality is equal, at least two experimental units
|
|
364
|
+
exist, and the prospective positive graph is complete bipartite.
|
|
365
|
+
|
|
366
|
+
Passing this audit authorizes the assignment law. It does not identify a
|
|
367
|
+
card-versus-no-memory effect: every lane may still receive another active
|
|
368
|
+
card, and one realized block supplies no matched neutral potential outcome.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
matching_input: LaneCardMatchingInput
|
|
372
|
+
status: CampaignDiagnosticCompatibilityStatus = field(init=False)
|
|
373
|
+
missing_pairs: tuple[tuple[str, str], ...] = field(init=False)
|
|
374
|
+
audit_sha256: str = field(init=False)
|
|
375
|
+
policy_id: str = field(
|
|
376
|
+
init=False,
|
|
377
|
+
default=CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_ID,
|
|
378
|
+
)
|
|
379
|
+
policy_version: int = field(
|
|
380
|
+
init=False,
|
|
381
|
+
default=CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_VERSION,
|
|
382
|
+
)
|
|
383
|
+
policy_definition_sha256: str = field(
|
|
384
|
+
init=False,
|
|
385
|
+
default=CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_DEFINITION_SHA256,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
def __post_init__(self) -> None:
|
|
389
|
+
if type(self.matching_input) is not LaneCardMatchingInput:
|
|
390
|
+
raise TypeError("matching_input must be exact LaneCardMatchingInput")
|
|
391
|
+
self.matching_input.__post_init__()
|
|
392
|
+
observed = {
|
|
393
|
+
(value.lane_id, value.card_key)
|
|
394
|
+
for value in self.matching_input.compatibilities
|
|
395
|
+
}
|
|
396
|
+
expected = tuple(
|
|
397
|
+
(lane.lane_id, card.card_key)
|
|
398
|
+
for lane in self.matching_input.lanes
|
|
399
|
+
for card in self.matching_input.cards
|
|
400
|
+
)
|
|
401
|
+
missing = tuple(value for value in expected if value not in observed)
|
|
402
|
+
eligible = (
|
|
403
|
+
len(self.matching_input.lanes) >= 2
|
|
404
|
+
and len(self.matching_input.lanes) == len(self.matching_input.cards)
|
|
405
|
+
and not missing
|
|
406
|
+
)
|
|
407
|
+
object.__setattr__(self, "missing_pairs", missing)
|
|
408
|
+
object.__setattr__(
|
|
409
|
+
self,
|
|
410
|
+
"status",
|
|
411
|
+
(
|
|
412
|
+
CampaignDiagnosticCompatibilityStatus.ELIGIBLE
|
|
413
|
+
if eligible
|
|
414
|
+
else CampaignDiagnosticCompatibilityStatus.INELIGIBLE
|
|
415
|
+
),
|
|
416
|
+
)
|
|
417
|
+
object.__setattr__(
|
|
418
|
+
self,
|
|
419
|
+
"audit_sha256",
|
|
420
|
+
hashlib.sha256(
|
|
421
|
+
_COMPATIBILITY_AUDIT_DOMAIN + _canonical_json(self._unsigned_record())
|
|
422
|
+
).hexdigest(),
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
@property
|
|
426
|
+
def eligible(self) -> bool:
|
|
427
|
+
return self.status is CampaignDiagnosticCompatibilityStatus.ELIGIBLE
|
|
428
|
+
|
|
429
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
430
|
+
return {
|
|
431
|
+
"schema_version": 1,
|
|
432
|
+
"policy": {
|
|
433
|
+
"policy_id": self.policy_id,
|
|
434
|
+
"policy_version": self.policy_version,
|
|
435
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
436
|
+
},
|
|
437
|
+
"matching_input_sha256": self.matching_input.input_sha256,
|
|
438
|
+
"compatibility_graph_sha256": (
|
|
439
|
+
self.matching_input.compatibility_graph_sha256
|
|
440
|
+
),
|
|
441
|
+
"lane_count": len(self.matching_input.lanes),
|
|
442
|
+
"card_count": len(self.matching_input.cards),
|
|
443
|
+
"observed_positive_edge_count": len(
|
|
444
|
+
self.matching_input.compatibilities
|
|
445
|
+
),
|
|
446
|
+
"required_complete_edge_count": (
|
|
447
|
+
len(self.matching_input.lanes) * len(self.matching_input.cards)
|
|
448
|
+
),
|
|
449
|
+
"minimum_two_units": len(self.matching_input.lanes) >= 2,
|
|
450
|
+
"balanced_cardinality": (
|
|
451
|
+
len(self.matching_input.lanes) == len(self.matching_input.cards)
|
|
452
|
+
),
|
|
453
|
+
"complete_bipartite_support": not self.missing_pairs,
|
|
454
|
+
"missing_pairs": [
|
|
455
|
+
{"lane_id": lane_id, "card_key": card_key}
|
|
456
|
+
for lane_id, card_key in self.missing_pairs
|
|
457
|
+
],
|
|
458
|
+
"status": self.status.value,
|
|
459
|
+
"randomized_singleton_assignment_allowed": self.eligible,
|
|
460
|
+
"card_vs_neutral_effect_identified": False,
|
|
461
|
+
"causal_credit_allowed": False,
|
|
462
|
+
"online_score_update_allowed": False,
|
|
463
|
+
"required_successor_design": (
|
|
464
|
+
"prospective_randomized_prompt_redacted_matched_control"
|
|
465
|
+
),
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
def to_record(self) -> dict[str, object]:
|
|
469
|
+
self.__post_init__()
|
|
470
|
+
return {**self._unsigned_record(), "audit_sha256": self.audit_sha256}
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
@dataclass(frozen=True, slots=True)
|
|
474
|
+
class CampaignDiagnosticCompleteSupportResolution:
|
|
475
|
+
"""Authenticated prospective resolution of one lane/card diagnostic block."""
|
|
476
|
+
|
|
477
|
+
full_matching_input: LaneCardMatchingInput
|
|
478
|
+
cohort_selection: CampaignDiagnosticCompleteSupportCohortSelection
|
|
479
|
+
matching: LaneCardMatchingReceipt
|
|
480
|
+
compatibility_audit: CampaignDiagnosticCompatibilityAudit
|
|
481
|
+
supports: tuple[CampaignDiagnosticDerivedSupportEdge, ...]
|
|
482
|
+
rejected_edges: tuple[CampaignDiagnosticRejectedSupportEdge, ...]
|
|
483
|
+
policy_id: str = field(
|
|
484
|
+
init=False,
|
|
485
|
+
default=CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_ID,
|
|
486
|
+
)
|
|
487
|
+
policy_version: int = field(
|
|
488
|
+
init=False,
|
|
489
|
+
default=CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_VERSION,
|
|
490
|
+
)
|
|
491
|
+
policy_definition_sha256: str = field(
|
|
492
|
+
init=False,
|
|
493
|
+
default=CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_DEFINITION_SHA256,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
def __post_init__(self) -> None:
|
|
497
|
+
if type(self.full_matching_input) is not LaneCardMatchingInput:
|
|
498
|
+
raise TypeError("full_matching_input must be exact LaneCardMatchingInput")
|
|
499
|
+
self.full_matching_input.__post_init__()
|
|
500
|
+
if type(self.cohort_selection) is not (
|
|
501
|
+
CampaignDiagnosticCompleteSupportCohortSelection
|
|
502
|
+
):
|
|
503
|
+
raise TypeError("cohort_selection must be exact")
|
|
504
|
+
self.cohort_selection.__post_init__()
|
|
505
|
+
if self.cohort_selection.matching_input != self.full_matching_input:
|
|
506
|
+
raise ValueError("cohort selection differs from the full support graph")
|
|
507
|
+
if type(self.matching) is not LaneCardMatchingReceipt:
|
|
508
|
+
raise TypeError("matching must be an exact LaneCardMatchingReceipt")
|
|
509
|
+
self.matching.__post_init__()
|
|
510
|
+
selected_input = self.cohort_selection.selected_matching_input
|
|
511
|
+
expected_matching_input = (
|
|
512
|
+
self.full_matching_input if selected_input is None else selected_input
|
|
513
|
+
)
|
|
514
|
+
if self.matching.matching_input != expected_matching_input:
|
|
515
|
+
raise ValueError("matching differs from selected diagnostic population")
|
|
516
|
+
if type(self.compatibility_audit) is not CampaignDiagnosticCompatibilityAudit:
|
|
517
|
+
raise TypeError("compatibility_audit must be exact")
|
|
518
|
+
self.compatibility_audit.__post_init__()
|
|
519
|
+
if self.compatibility_audit.matching_input != self.matching.matching_input:
|
|
520
|
+
raise ValueError("compatibility audit differs from the matching population")
|
|
521
|
+
if type(self.supports) is not tuple or any(
|
|
522
|
+
type(value) is not CampaignDiagnosticDerivedSupportEdge
|
|
523
|
+
for value in self.supports
|
|
524
|
+
):
|
|
525
|
+
raise TypeError("supports must be an exact support-edge tuple")
|
|
526
|
+
for value in self.supports:
|
|
527
|
+
value.__post_init__()
|
|
528
|
+
support_keys = tuple((value.lane_id, value.card_key) for value in self.supports)
|
|
529
|
+
if support_keys != tuple(sorted(set(support_keys))):
|
|
530
|
+
raise ValueError("supports must use unique canonical lane/card order")
|
|
531
|
+
graph_keys = tuple(
|
|
532
|
+
(value.lane_id, value.card_key)
|
|
533
|
+
for value in self.full_matching_input.compatibilities
|
|
534
|
+
)
|
|
535
|
+
if support_keys != graph_keys:
|
|
536
|
+
raise ValueError("derived supports differ from positive graph edges")
|
|
537
|
+
if type(self.rejected_edges) is not tuple or any(
|
|
538
|
+
type(value) is not CampaignDiagnosticRejectedSupportEdge
|
|
539
|
+
for value in self.rejected_edges
|
|
540
|
+
):
|
|
541
|
+
raise TypeError("rejected_edges must be an exact tuple")
|
|
542
|
+
for value in self.rejected_edges:
|
|
543
|
+
value.__post_init__()
|
|
544
|
+
rejected_keys = tuple(
|
|
545
|
+
(value.lane_id, value.card_key) for value in self.rejected_edges
|
|
546
|
+
)
|
|
547
|
+
if rejected_keys != tuple(sorted(set(rejected_keys))):
|
|
548
|
+
raise ValueError("rejected edges must use unique canonical lane/card order")
|
|
549
|
+
if set(support_keys).intersection(rejected_keys):
|
|
550
|
+
raise ValueError("one lane/card pair cannot be supported and rejected")
|
|
551
|
+
all_pairs = {
|
|
552
|
+
(lane.lane_id, card.card_key)
|
|
553
|
+
for lane in self.full_matching_input.lanes
|
|
554
|
+
for card in self.full_matching_input.cards
|
|
555
|
+
}
|
|
556
|
+
if set((*support_keys, *rejected_keys)) != all_pairs:
|
|
557
|
+
raise ValueError("support resolution must classify every lane/card pair")
|
|
558
|
+
if (
|
|
559
|
+
self.policy_id != CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_ID
|
|
560
|
+
or self.policy_version
|
|
561
|
+
!= CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_VERSION
|
|
562
|
+
or self.policy_definition_sha256
|
|
563
|
+
!= CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_DEFINITION_SHA256
|
|
564
|
+
):
|
|
565
|
+
raise ValueError("unsupported complete-support resolution policy")
|
|
566
|
+
|
|
567
|
+
@property
|
|
568
|
+
def eligible(self) -> bool:
|
|
569
|
+
self.__post_init__()
|
|
570
|
+
return (
|
|
571
|
+
self.cohort_selection.eligible
|
|
572
|
+
and self.matching.is_full
|
|
573
|
+
and self.compatibility_audit.eligible
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
def support_for(
|
|
577
|
+
self,
|
|
578
|
+
lane_id: str,
|
|
579
|
+
card_key: str,
|
|
580
|
+
) -> PortfolioMemoryDoseCardSupport:
|
|
581
|
+
self.__post_init__()
|
|
582
|
+
values = tuple(
|
|
583
|
+
value.support
|
|
584
|
+
for value in self.supports
|
|
585
|
+
if value.lane_id == lane_id and value.card_key == card_key
|
|
586
|
+
)
|
|
587
|
+
if len(values) != 1:
|
|
588
|
+
raise KeyError("lane/card pair has no derived positive support")
|
|
589
|
+
return values[0]
|
|
590
|
+
|
|
591
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
592
|
+
return {
|
|
593
|
+
"schema_version": 1,
|
|
594
|
+
"policy": {
|
|
595
|
+
"policy_id": self.policy_id,
|
|
596
|
+
"policy_version": self.policy_version,
|
|
597
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
598
|
+
},
|
|
599
|
+
"full_matching_input": self.full_matching_input.to_record(),
|
|
600
|
+
"cohort_selection": self.cohort_selection.to_record(),
|
|
601
|
+
"matching": self.matching.to_record(),
|
|
602
|
+
"compatibility_audit": self.compatibility_audit.to_record(),
|
|
603
|
+
"supports": [value.to_record() for value in self.supports],
|
|
604
|
+
"rejected_edges": [
|
|
605
|
+
value.to_record() for value in self.rejected_edges
|
|
606
|
+
],
|
|
607
|
+
"eligible": self.eligible,
|
|
608
|
+
"provider_fields_consulted": False,
|
|
609
|
+
"outcome_values_consulted": False,
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
@property
|
|
613
|
+
def receipt_sha256(self) -> str:
|
|
614
|
+
return hashlib.sha256(
|
|
615
|
+
_SUPPORT_RESOLUTION_DOMAIN + _canonical_json(self._unsigned_record())
|
|
616
|
+
).hexdigest()
|
|
617
|
+
|
|
618
|
+
def to_record(self) -> dict[str, object]:
|
|
619
|
+
self.__post_init__()
|
|
620
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
@dataclass(frozen=True, slots=True)
|
|
624
|
+
class CampaignDiagnosticCompleteSupportResolver:
|
|
625
|
+
"""Derive and resolve a complete-support cohort before provider dispatch."""
|
|
626
|
+
|
|
627
|
+
def resolve(
|
|
628
|
+
self,
|
|
629
|
+
*,
|
|
630
|
+
lanes: tuple[CampaignDiagnosticSupportLaneInput, ...],
|
|
631
|
+
cards: tuple[CampaignDiagnosticSupportCardInput, ...],
|
|
632
|
+
cohort_size: int,
|
|
633
|
+
selection_key_sha256: str,
|
|
634
|
+
) -> CampaignDiagnosticCompleteSupportResolution:
|
|
635
|
+
if type(lanes) is not tuple or not lanes or any(
|
|
636
|
+
type(value) is not CampaignDiagnosticSupportLaneInput for value in lanes
|
|
637
|
+
):
|
|
638
|
+
raise ValueError("lanes must be a non-empty exact support-lane tuple")
|
|
639
|
+
if type(cards) is not tuple or not cards or any(
|
|
640
|
+
type(value) is not CampaignDiagnosticSupportCardInput for value in cards
|
|
641
|
+
):
|
|
642
|
+
raise ValueError("cards must be a non-empty exact support-card tuple")
|
|
643
|
+
for value in lanes:
|
|
644
|
+
value.__post_init__()
|
|
645
|
+
for value in cards:
|
|
646
|
+
value.__post_init__()
|
|
647
|
+
lane_ids = tuple(value.lane.lane_id for value in lanes)
|
|
648
|
+
card_keys = tuple(value.card.card_key for value in cards)
|
|
649
|
+
if lane_ids != tuple(sorted(set(lane_ids))):
|
|
650
|
+
raise ValueError("support lanes must be unique and canonical")
|
|
651
|
+
if card_keys != tuple(sorted(set(card_keys))):
|
|
652
|
+
raise ValueError("support cards must be unique and canonical")
|
|
653
|
+
if type(cohort_size) is not int or cohort_size != len(lanes):
|
|
654
|
+
raise ValueError("cohort_size must exactly equal the stable lane count")
|
|
655
|
+
require_sha256(selection_key_sha256, "selection_key_sha256")
|
|
656
|
+
|
|
657
|
+
supports: list[CampaignDiagnosticDerivedSupportEdge] = []
|
|
658
|
+
rejected: list[CampaignDiagnosticRejectedSupportEdge] = []
|
|
659
|
+
for lane in lanes:
|
|
660
|
+
for card in cards:
|
|
661
|
+
try:
|
|
662
|
+
support = derive_portfolio_memory_dose_card_support(
|
|
663
|
+
card.semantics,
|
|
664
|
+
lane.finite_variation_contract,
|
|
665
|
+
)
|
|
666
|
+
except ValueError as error:
|
|
667
|
+
rejected.append(
|
|
668
|
+
CampaignDiagnosticRejectedSupportEdge(
|
|
669
|
+
lane_id=lane.lane.lane_id,
|
|
670
|
+
card_key=card.card.card_key,
|
|
671
|
+
reason_sha256=hashlib.sha256(
|
|
672
|
+
str(error).encode("utf-8", errors="strict")
|
|
673
|
+
).hexdigest(),
|
|
674
|
+
)
|
|
675
|
+
)
|
|
676
|
+
continue
|
|
677
|
+
supports.append(
|
|
678
|
+
CampaignDiagnosticDerivedSupportEdge(
|
|
679
|
+
lane_id=lane.lane.lane_id,
|
|
680
|
+
card_key=card.card.card_key,
|
|
681
|
+
support=support,
|
|
682
|
+
)
|
|
683
|
+
)
|
|
684
|
+
canonical_supports = tuple(
|
|
685
|
+
sorted(supports, key=lambda value: (value.lane_id, value.card_key))
|
|
686
|
+
)
|
|
687
|
+
canonical_rejected = tuple(
|
|
688
|
+
sorted(rejected, key=lambda value: (value.lane_id, value.card_key))
|
|
689
|
+
)
|
|
690
|
+
matching_input = LaneCardMatchingInput(
|
|
691
|
+
lanes=tuple(value.lane for value in lanes),
|
|
692
|
+
cards=tuple(value.card for value in cards),
|
|
693
|
+
compatibilities=tuple(
|
|
694
|
+
LaneCardCompatibility(
|
|
695
|
+
lane_id=value.lane_id,
|
|
696
|
+
card_key=value.card_key,
|
|
697
|
+
compatibility_evidence_sha256=value.evidence_sha256,
|
|
698
|
+
)
|
|
699
|
+
for value in canonical_supports
|
|
700
|
+
),
|
|
701
|
+
)
|
|
702
|
+
selection = CampaignDiagnosticCompleteSupportCohortSelection(
|
|
703
|
+
matching_input=matching_input,
|
|
704
|
+
cohort_size=cohort_size,
|
|
705
|
+
selection_key_sha256=selection_key_sha256,
|
|
706
|
+
)
|
|
707
|
+
selected_input = selection.selected_matching_input
|
|
708
|
+
diagnostic_input = matching_input if selected_input is None else selected_input
|
|
709
|
+
matching = CanonicalLaneCardMatchingPlanner().plan(
|
|
710
|
+
lanes=diagnostic_input.lanes,
|
|
711
|
+
cards=diagnostic_input.cards,
|
|
712
|
+
compatibilities=diagnostic_input.compatibilities,
|
|
713
|
+
)
|
|
714
|
+
audit = CampaignDiagnosticCompatibilityAudit(matching.matching_input)
|
|
715
|
+
return CampaignDiagnosticCompleteSupportResolution(
|
|
716
|
+
full_matching_input=matching_input,
|
|
717
|
+
cohort_selection=selection,
|
|
718
|
+
matching=matching,
|
|
719
|
+
compatibility_audit=audit,
|
|
720
|
+
supports=canonical_supports,
|
|
721
|
+
rejected_edges=canonical_rejected,
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def _canonical_references(
|
|
726
|
+
values: tuple[InsightRef, ...],
|
|
727
|
+
*,
|
|
728
|
+
name: str,
|
|
729
|
+
empty: bool,
|
|
730
|
+
) -> tuple[InsightRef, ...]:
|
|
731
|
+
if type(values) is not tuple or any(
|
|
732
|
+
type(value) is not InsightRef for value in values
|
|
733
|
+
):
|
|
734
|
+
raise TypeError(f"{name} must be an exact tuple of InsightRef values")
|
|
735
|
+
for value in values:
|
|
736
|
+
InsightRef.__post_init__(value)
|
|
737
|
+
if values != tuple(sorted(set(values))):
|
|
738
|
+
raise ValueError(f"{name} must be unique and canonical")
|
|
739
|
+
if not empty and not values:
|
|
740
|
+
raise ValueError(f"{name} must be non-empty")
|
|
741
|
+
return values
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
@dataclass(frozen=True, slots=True)
|
|
745
|
+
class CampaignDiagnosticSingletonBlock:
|
|
746
|
+
"""Authenticated complete singleton block over active and control cards."""
|
|
747
|
+
|
|
748
|
+
active_references: tuple[InsightRef, ...]
|
|
749
|
+
control_references: tuple[InsightRef, ...]
|
|
750
|
+
assignment_plan: BalancedSubsetBlockPlan
|
|
751
|
+
policy_id: str = field(
|
|
752
|
+
init=False,
|
|
753
|
+
default=CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_ID,
|
|
754
|
+
)
|
|
755
|
+
policy_version: int = field(
|
|
756
|
+
init=False,
|
|
757
|
+
default=CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_VERSION,
|
|
758
|
+
)
|
|
759
|
+
policy_definition_sha256: str = field(
|
|
760
|
+
init=False,
|
|
761
|
+
default=CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_DEFINITION_SHA256,
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
def __post_init__(self) -> None:
|
|
765
|
+
active = _canonical_references(
|
|
766
|
+
self.active_references,
|
|
767
|
+
name="active_references",
|
|
768
|
+
empty=False,
|
|
769
|
+
)
|
|
770
|
+
controls = _canonical_references(
|
|
771
|
+
self.control_references,
|
|
772
|
+
name="control_references",
|
|
773
|
+
empty=True,
|
|
774
|
+
)
|
|
775
|
+
if set(active).intersection(controls):
|
|
776
|
+
raise ValueError("active and control references must be disjoint")
|
|
777
|
+
if type(self.assignment_plan) is not BalancedSubsetBlockPlan:
|
|
778
|
+
raise TypeError("assignment_plan must be exact BalancedSubsetBlockPlan")
|
|
779
|
+
self.assignment_plan.__post_init__()
|
|
780
|
+
eligible = tuple(
|
|
781
|
+
entry.reference for entry in self.assignment_plan.snapshot.entries
|
|
782
|
+
)
|
|
783
|
+
if eligible != tuple(sorted((*active, *controls))):
|
|
784
|
+
raise ValueError("assignment snapshot differs from the diagnostic cohort")
|
|
785
|
+
if (
|
|
786
|
+
self.assignment_plan.subset_size != 1
|
|
787
|
+
or self.assignment_plan.full_block_count != 1
|
|
788
|
+
or self.assignment_plan.remainder_size != 0
|
|
789
|
+
or len(self.assignment_plan.ordered_units) != len(eligible)
|
|
790
|
+
):
|
|
791
|
+
raise ValueError(
|
|
792
|
+
"diagnostic singleton design must be exactly one complete block"
|
|
793
|
+
)
|
|
794
|
+
if (
|
|
795
|
+
self.policy_id != CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_ID
|
|
796
|
+
or self.policy_version != CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_VERSION
|
|
797
|
+
or self.policy_definition_sha256
|
|
798
|
+
!= CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_DEFINITION_SHA256
|
|
799
|
+
):
|
|
800
|
+
raise ValueError("unsupported diagnostic singleton block policy")
|
|
801
|
+
|
|
802
|
+
@property
|
|
803
|
+
def plan_sha256(self) -> str:
|
|
804
|
+
self.__post_init__()
|
|
805
|
+
return self.assignment_plan.receipt_sha256
|
|
806
|
+
|
|
807
|
+
def to_record(self) -> dict[str, object]:
|
|
808
|
+
self.__post_init__()
|
|
809
|
+
return {
|
|
810
|
+
"schema_version": 1,
|
|
811
|
+
"policy": {
|
|
812
|
+
"policy_id": self.policy_id,
|
|
813
|
+
"policy_version": self.policy_version,
|
|
814
|
+
"definition_sha256": self.policy_definition_sha256,
|
|
815
|
+
},
|
|
816
|
+
"active_references": [
|
|
817
|
+
{
|
|
818
|
+
"insight_id": value.insight_id.value,
|
|
819
|
+
"version": value.version,
|
|
820
|
+
}
|
|
821
|
+
for value in self.active_references
|
|
822
|
+
],
|
|
823
|
+
"control_references": [
|
|
824
|
+
{
|
|
825
|
+
"insight_id": value.insight_id.value,
|
|
826
|
+
"version": value.version,
|
|
827
|
+
}
|
|
828
|
+
for value in self.control_references
|
|
829
|
+
],
|
|
830
|
+
"assignment_plan": self.assignment_plan.to_record(),
|
|
831
|
+
"plan_sha256": self.plan_sha256,
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
@dataclass(frozen=True, slots=True)
|
|
836
|
+
class CampaignDiagnosticSingletonBlockPlanner:
|
|
837
|
+
"""Fill stable lanes and delegate exact randomization to the block policy."""
|
|
838
|
+
|
|
839
|
+
def plan(
|
|
840
|
+
self,
|
|
841
|
+
*,
|
|
842
|
+
active_references: tuple[InsightRef, ...],
|
|
843
|
+
control_references: tuple[InsightRef, ...],
|
|
844
|
+
exact_context_sha256: str,
|
|
845
|
+
estimand_stratum_sha256: str,
|
|
846
|
+
ordered_units: tuple[StableMemoryAssignmentUnit, ...],
|
|
847
|
+
full_block_permutation_rank: int,
|
|
848
|
+
) -> CampaignDiagnosticSingletonBlock:
|
|
849
|
+
active = _canonical_references(
|
|
850
|
+
active_references,
|
|
851
|
+
name="active_references",
|
|
852
|
+
empty=False,
|
|
853
|
+
)
|
|
854
|
+
controls = _canonical_references(
|
|
855
|
+
control_references,
|
|
856
|
+
name="control_references",
|
|
857
|
+
empty=True,
|
|
858
|
+
)
|
|
859
|
+
if set(active).intersection(controls):
|
|
860
|
+
raise ValueError("active and control references must be disjoint")
|
|
861
|
+
require_sha256(exact_context_sha256, "exact_context_sha256")
|
|
862
|
+
require_sha256(estimand_stratum_sha256, "estimand_stratum_sha256")
|
|
863
|
+
if (
|
|
864
|
+
type(ordered_units) is not tuple
|
|
865
|
+
or len(ordered_units) < 2
|
|
866
|
+
or any(
|
|
867
|
+
type(value) is not StableMemoryAssignmentUnit for value in ordered_units
|
|
868
|
+
)
|
|
869
|
+
):
|
|
870
|
+
raise ValueError(
|
|
871
|
+
"ordered_units must contain at least two exact stable lanes"
|
|
872
|
+
)
|
|
873
|
+
for value in ordered_units:
|
|
874
|
+
StableMemoryAssignmentUnit.__post_init__(value)
|
|
875
|
+
eligible = tuple(sorted((*active, *controls)))
|
|
876
|
+
if len(eligible) != len(ordered_units):
|
|
877
|
+
raise ValueError(
|
|
878
|
+
"active plus control references must exactly fill the stable lanes"
|
|
879
|
+
)
|
|
880
|
+
if type(full_block_permutation_rank) is not int or not (
|
|
881
|
+
0 <= full_block_permutation_rank < math.factorial(len(eligible))
|
|
882
|
+
):
|
|
883
|
+
raise ValueError("full_block_permutation_rank is outside the exact law")
|
|
884
|
+
snapshot = CausalSearchScorePolicy(
|
|
885
|
+
uncertainty_scale=0.0,
|
|
886
|
+
exploration_weight=0.0,
|
|
887
|
+
).genesis(
|
|
888
|
+
exact_context_hash=exact_context_sha256,
|
|
889
|
+
estimand_stratum_hash=estimand_stratum_sha256,
|
|
890
|
+
priors={reference: 0.0 for reference in eligible},
|
|
891
|
+
)
|
|
892
|
+
assignment_plan = BalancedSubsetBlockPlanner().plan(
|
|
893
|
+
snapshot=snapshot,
|
|
894
|
+
ordered_units=ordered_units,
|
|
895
|
+
subset_size=1,
|
|
896
|
+
full_block_permutation_ranks=(full_block_permutation_rank,),
|
|
897
|
+
)
|
|
898
|
+
return CampaignDiagnosticSingletonBlock(
|
|
899
|
+
active_references=active,
|
|
900
|
+
control_references=controls,
|
|
901
|
+
assignment_plan=assignment_plan,
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
__all__ = [
|
|
906
|
+
"CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_DEFINITION_SHA256",
|
|
907
|
+
"CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_ID",
|
|
908
|
+
"CAMPAIGN_DIAGNOSTIC_COHORT_SELECTION_POLICY_VERSION",
|
|
909
|
+
"CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_DEFINITION_SHA256",
|
|
910
|
+
"CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_ID",
|
|
911
|
+
"CAMPAIGN_DIAGNOSTIC_COMPATIBILITY_AUDIT_POLICY_VERSION",
|
|
912
|
+
"CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_DEFINITION_SHA256",
|
|
913
|
+
"CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_ID",
|
|
914
|
+
"CAMPAIGN_DIAGNOSTIC_SINGLETON_BLOCK_POLICY_VERSION",
|
|
915
|
+
"CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_DEFINITION_SHA256",
|
|
916
|
+
"CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_ID",
|
|
917
|
+
"CAMPAIGN_DIAGNOSTIC_SUPPORT_RESOLUTION_POLICY_VERSION",
|
|
918
|
+
"CampaignDiagnosticCompleteSupportResolution",
|
|
919
|
+
"CampaignDiagnosticCompleteSupportResolver",
|
|
920
|
+
"CampaignDiagnosticSingletonBlock",
|
|
921
|
+
"CampaignDiagnosticSingletonBlockPlanner",
|
|
922
|
+
"CampaignDiagnosticCohortSelectionStatus",
|
|
923
|
+
"CampaignDiagnosticCompleteSupportCohortSelection",
|
|
924
|
+
"CampaignDiagnosticCompatibilityAudit",
|
|
925
|
+
"CampaignDiagnosticCompatibilityStatus",
|
|
926
|
+
"CampaignDiagnosticDerivedSupportEdge",
|
|
927
|
+
"CampaignDiagnosticRejectedSupportEdge",
|
|
928
|
+
"CampaignDiagnosticSupportCardInput",
|
|
929
|
+
"CampaignDiagnosticSupportLaneInput",
|
|
930
|
+
]
|