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,1153 @@
|
|
|
1
|
+
"""Authenticated forecast frames and audited allocation-surface ports.
|
|
2
|
+
|
|
3
|
+
The original allocation port deliberately accepts only a complete resolved
|
|
4
|
+
forecast batch. A partition block must not be relabelled as such a batch just
|
|
5
|
+
to reach an allocator. This module provides a parallel, provider-neutral
|
|
6
|
+
boundary whose source remains an exact complete batch, partition block, or
|
|
7
|
+
authenticated subset of one partition block.
|
|
8
|
+
|
|
9
|
+
No benchmark metric or provider representation is interpreted here. A bound
|
|
10
|
+
diagnostic policy supplies the single domain-dependent judgement used by the
|
|
11
|
+
surface gate: whether one candidate score is at a benchmark-defined boundary
|
|
12
|
+
or extreme. Counts, ties, gaps, and score-multiset identities remain trusted
|
|
13
|
+
generic arithmetic.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import hashlib
|
|
19
|
+
import json
|
|
20
|
+
import math
|
|
21
|
+
import re
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from enum import Enum
|
|
24
|
+
from typing import Protocol, runtime_checkable
|
|
25
|
+
|
|
26
|
+
from agent_evolve.domain.patch import require_sha256
|
|
27
|
+
from agent_evolve.ports.action_allocation import (
|
|
28
|
+
AllocatedActionMember,
|
|
29
|
+
ForecastPortfolioUtilityBinding,
|
|
30
|
+
PortfolioAllocationScore,
|
|
31
|
+
)
|
|
32
|
+
from agent_evolve.ports.action_forecast import (
|
|
33
|
+
ActionForecastBlockRequest,
|
|
34
|
+
ActionForecastRequest,
|
|
35
|
+
ResolvedActionForecast,
|
|
36
|
+
ResolvedActionForecastBatch,
|
|
37
|
+
ResolvedActionForecastBlock,
|
|
38
|
+
validate_resolved_action_forecast_block,
|
|
39
|
+
validate_resolved_action_forecasts,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
44
|
+
_OPTION_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,255}$")
|
|
45
|
+
_CANDIDATE_LABEL = re.compile(r"^row_[0-9]{8}$")
|
|
46
|
+
_FRAME_DOMAIN = b"agent-evolve:resolved-action-allocation-frame:v1\x00"
|
|
47
|
+
_SUBSET_POLICY_DOMAIN = b"agent-evolve:allocation-frame-subset-policy:v1\x00"
|
|
48
|
+
_ELIGIBLE_DOMAIN = b"agent-evolve:frame-allocation-eligible-set:v1\x00"
|
|
49
|
+
_REQUEST_DOMAIN = b"agent-evolve:frame-action-allocation-request:v1\x00"
|
|
50
|
+
_DECISION_DOMAIN = b"agent-evolve:frame-action-portfolio-decision:v1\x00"
|
|
51
|
+
_DIAGNOSTIC_POLICY_DOMAIN = (
|
|
52
|
+
b"agent-evolve:allocation-score-diagnostic-policy:v1\x00"
|
|
53
|
+
)
|
|
54
|
+
_GATE_POLICY_DOMAIN = b"agent-evolve:allocation-surface-gate-policy:v1\x00"
|
|
55
|
+
_SCORE_MULTISET_DOMAIN = b"agent-evolve:allocation-score-multiset:v1\x00"
|
|
56
|
+
_STEP_AUDIT_DOMAIN = b"agent-evolve:allocation-surface-step-audit:v1\x00"
|
|
57
|
+
_SURFACE_AUDIT_DOMAIN = b"agent-evolve:allocation-surface-audit:v1\x00"
|
|
58
|
+
_RESULT_DOMAIN = b"agent-evolve:audited-frame-allocation-result:v1\x00"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _canonical_json(value: object) -> bytes:
|
|
62
|
+
return json.dumps(
|
|
63
|
+
value,
|
|
64
|
+
allow_nan=False,
|
|
65
|
+
ensure_ascii=True,
|
|
66
|
+
separators=(",", ":"),
|
|
67
|
+
sort_keys=True,
|
|
68
|
+
).encode("ascii")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
72
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _finite_float(value: object, name: str) -> float:
|
|
76
|
+
if type(value) is not float or not math.isfinite(value):
|
|
77
|
+
raise TypeError(f"{name} must be a finite canonical float")
|
|
78
|
+
return value
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _token(value: object, name: str) -> str:
|
|
82
|
+
if type(value) is not str or _TOKEN.fullmatch(value) is None:
|
|
83
|
+
raise ValueError(f"{name} must use the closed token grammar")
|
|
84
|
+
return value
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _canonical_parent_receipts(values: tuple[str, ...]) -> None:
|
|
88
|
+
if type(values) is not tuple or any(type(value) is not str for value in values):
|
|
89
|
+
raise TypeError("parent_receipt_sha256s must be an exact digest tuple")
|
|
90
|
+
for index, value in enumerate(values):
|
|
91
|
+
require_sha256(value, f"parent_receipt_sha256s[{index}]")
|
|
92
|
+
if values != tuple(sorted(set(values))):
|
|
93
|
+
raise ValueError("parent receipts must be unique and canonically sorted")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ActionForecastAllocationFrameKind(str, Enum):
|
|
97
|
+
COMPLETE = "complete"
|
|
98
|
+
PARTITION_BLOCK = "partition_block"
|
|
99
|
+
PARTITION_BLOCK_SUBSET = "partition_block_subset"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
103
|
+
class ActionAllocationFrameSubsetPolicyBinding:
|
|
104
|
+
"""Identified rule selecting allocation rows from one resolved block."""
|
|
105
|
+
|
|
106
|
+
policy_id: str
|
|
107
|
+
policy_version: int
|
|
108
|
+
policy_definition_sha256: str
|
|
109
|
+
|
|
110
|
+
def __post_init__(self) -> None:
|
|
111
|
+
_token(self.policy_id, "policy_id")
|
|
112
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
113
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
114
|
+
require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
|
|
115
|
+
|
|
116
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
117
|
+
self.__post_init__()
|
|
118
|
+
return {
|
|
119
|
+
"schema_version": 1,
|
|
120
|
+
"policy_id": self.policy_id,
|
|
121
|
+
"policy_version": self.policy_version,
|
|
122
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def binding_sha256(self) -> str:
|
|
127
|
+
return _hash(_SUBSET_POLICY_DOMAIN, self._unsigned_record())
|
|
128
|
+
|
|
129
|
+
def to_record(self) -> dict[str, object]:
|
|
130
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
131
|
+
|
|
132
|
+
def __eq__(self, other: object) -> bool:
|
|
133
|
+
return (
|
|
134
|
+
type(self) is ActionAllocationFrameSubsetPolicyBinding
|
|
135
|
+
and type(other) is ActionAllocationFrameSubsetPolicyBinding
|
|
136
|
+
and self.binding_sha256 == other.binding_sha256
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
__hash__ = None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
143
|
+
class ResolvedActionForecastAllocationFrame:
|
|
144
|
+
"""Exact allocation view over a complete batch, block, or block subset.
|
|
145
|
+
|
|
146
|
+
Source objects are retained so public validation can authenticate forecast
|
|
147
|
+
values rather than trusting copied IDs. The durable record stores only the
|
|
148
|
+
source receipt and exact ordered row/option identities.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
request: ActionForecastRequest = field(repr=False, compare=False)
|
|
152
|
+
frame_kind: ActionForecastAllocationFrameKind
|
|
153
|
+
global_row_indices: tuple[int, ...]
|
|
154
|
+
parent_receipt_sha256s: tuple[str, ...]
|
|
155
|
+
complete_batch: ResolvedActionForecastBatch | None = field(
|
|
156
|
+
default=None,
|
|
157
|
+
repr=False,
|
|
158
|
+
compare=False,
|
|
159
|
+
)
|
|
160
|
+
block_request: ActionForecastBlockRequest | None = field(
|
|
161
|
+
default=None,
|
|
162
|
+
repr=False,
|
|
163
|
+
compare=False,
|
|
164
|
+
)
|
|
165
|
+
resolved_block: ResolvedActionForecastBlock | None = field(
|
|
166
|
+
default=None,
|
|
167
|
+
repr=False,
|
|
168
|
+
compare=False,
|
|
169
|
+
)
|
|
170
|
+
subset_policy: ActionAllocationFrameSubsetPolicyBinding | None = None
|
|
171
|
+
_validated_receipt_sha256: str | None = field(
|
|
172
|
+
default=None,
|
|
173
|
+
init=False,
|
|
174
|
+
repr=False,
|
|
175
|
+
compare=False,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def __post_init__(self) -> None:
|
|
179
|
+
if self._validated_receipt_sha256 is not None:
|
|
180
|
+
require_sha256(
|
|
181
|
+
self._validated_receipt_sha256,
|
|
182
|
+
"_validated_receipt_sha256",
|
|
183
|
+
)
|
|
184
|
+
return
|
|
185
|
+
if type(self.request) is not ActionForecastRequest:
|
|
186
|
+
raise TypeError("request must be an exact ActionForecastRequest")
|
|
187
|
+
self.request.__post_init__()
|
|
188
|
+
if type(self.frame_kind) is not ActionForecastAllocationFrameKind:
|
|
189
|
+
raise TypeError("frame_kind must be an exact allocation-frame kind")
|
|
190
|
+
if type(self.global_row_indices) is not tuple or not self.global_row_indices:
|
|
191
|
+
raise ValueError("global_row_indices must be a non-empty exact tuple")
|
|
192
|
+
if any(type(value) is not int for value in self.global_row_indices):
|
|
193
|
+
raise TypeError("global_row_indices must contain exact integers")
|
|
194
|
+
if self.global_row_indices != tuple(sorted(set(self.global_row_indices))):
|
|
195
|
+
raise ValueError("global rows must be unique and in global order")
|
|
196
|
+
contract = self.request.finite_variation_contract
|
|
197
|
+
if any(value < 0 or value >= len(contract.options) for value in self.global_row_indices):
|
|
198
|
+
raise ValueError("an allocation-frame row is outside the finite contract")
|
|
199
|
+
_canonical_parent_receipts(self.parent_receipt_sha256s)
|
|
200
|
+
|
|
201
|
+
if self.frame_kind is ActionForecastAllocationFrameKind.COMPLETE:
|
|
202
|
+
if type(self.complete_batch) is not ResolvedActionForecastBatch:
|
|
203
|
+
raise TypeError("complete frames require an exact resolved batch")
|
|
204
|
+
if self.block_request is not None or self.resolved_block is not None:
|
|
205
|
+
raise ValueError("complete frames forbid partition-block sources")
|
|
206
|
+
if self.subset_policy is not None:
|
|
207
|
+
raise ValueError("complete frames forbid a subset policy")
|
|
208
|
+
validate_resolved_action_forecasts(self.request, self.complete_batch)
|
|
209
|
+
if self.global_row_indices != tuple(range(len(contract.options))):
|
|
210
|
+
raise ValueError("complete frames must cover every global row")
|
|
211
|
+
else:
|
|
212
|
+
if self.complete_batch is not None:
|
|
213
|
+
raise ValueError("partition frames forbid a complete batch")
|
|
214
|
+
if type(self.block_request) is not ActionForecastBlockRequest:
|
|
215
|
+
raise TypeError("partition frames require an exact block request")
|
|
216
|
+
if type(self.resolved_block) is not ResolvedActionForecastBlock:
|
|
217
|
+
raise TypeError("partition frames require an exact resolved block")
|
|
218
|
+
if self.block_request.request != self.request:
|
|
219
|
+
raise ValueError("block request carries another forecast request")
|
|
220
|
+
validate_resolved_action_forecast_block(
|
|
221
|
+
self.block_request,
|
|
222
|
+
self.resolved_block,
|
|
223
|
+
)
|
|
224
|
+
spec = self.block_request.block
|
|
225
|
+
full_indices = tuple(range(spec.global_row_start, spec.global_row_stop))
|
|
226
|
+
if self.frame_kind is ActionForecastAllocationFrameKind.PARTITION_BLOCK:
|
|
227
|
+
if self.global_row_indices != full_indices:
|
|
228
|
+
raise ValueError("full block frame differs from its block rows")
|
|
229
|
+
if self.subset_policy is not None:
|
|
230
|
+
raise ValueError("full block frames forbid a subset policy")
|
|
231
|
+
else:
|
|
232
|
+
if type(self.subset_policy) is not (
|
|
233
|
+
ActionAllocationFrameSubsetPolicyBinding
|
|
234
|
+
):
|
|
235
|
+
raise TypeError("block-subset frames require a subset policy")
|
|
236
|
+
self.subset_policy.__post_init__()
|
|
237
|
+
if not self.parent_receipt_sha256s:
|
|
238
|
+
raise ValueError(
|
|
239
|
+
"block-subset frames require an authenticated parent receipt"
|
|
240
|
+
)
|
|
241
|
+
if not set(self.global_row_indices).issubset(full_indices):
|
|
242
|
+
raise ValueError("block-subset rows escape the resolved block")
|
|
243
|
+
|
|
244
|
+
expected = tuple(contract.options[index] for index in self.global_row_indices)
|
|
245
|
+
observed = self.forecasts
|
|
246
|
+
if len(observed) != len(expected):
|
|
247
|
+
raise ValueError("frame forecasts differ from its exact row count")
|
|
248
|
+
for option, forecast in zip(expected, observed, strict=True):
|
|
249
|
+
if (
|
|
250
|
+
forecast.option_id != option.option_id
|
|
251
|
+
or forecast.option_identity_sha256 != option.identity_sha256
|
|
252
|
+
or forecast.child_configuration_sha256
|
|
253
|
+
!= option.child_configuration_sha256
|
|
254
|
+
or forecast.family != option.family
|
|
255
|
+
):
|
|
256
|
+
raise ValueError("allocation frame changed a finite option identity")
|
|
257
|
+
object.__setattr__(
|
|
258
|
+
self,
|
|
259
|
+
"_validated_receipt_sha256",
|
|
260
|
+
_hash(_FRAME_DOMAIN, self._record_from_validated_values()),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def forecasts(self) -> tuple[ResolvedActionForecast, ...]:
|
|
265
|
+
if self.frame_kind is ActionForecastAllocationFrameKind.COMPLETE:
|
|
266
|
+
assert self.complete_batch is not None
|
|
267
|
+
return self.complete_batch.forecasts
|
|
268
|
+
assert self.block_request is not None
|
|
269
|
+
assert self.resolved_block is not None
|
|
270
|
+
start = self.block_request.block.global_row_start
|
|
271
|
+
return tuple(
|
|
272
|
+
self.resolved_block.forecasts[index - start]
|
|
273
|
+
for index in self.global_row_indices
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def source_forecast_receipt_sha256(self) -> str:
|
|
278
|
+
if self.frame_kind is ActionForecastAllocationFrameKind.COMPLETE:
|
|
279
|
+
assert self.complete_batch is not None
|
|
280
|
+
return self.complete_batch.receipt_sha256
|
|
281
|
+
assert self.resolved_block is not None
|
|
282
|
+
return self.resolved_block.receipt_sha256
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def policy_identity(self) -> tuple[str, int, str]:
|
|
286
|
+
source = self.complete_batch or self.resolved_block
|
|
287
|
+
assert source is not None
|
|
288
|
+
return (
|
|
289
|
+
source.policy_id,
|
|
290
|
+
source.policy_version,
|
|
291
|
+
source.policy_definition_sha256,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def _record_from_validated_values(self) -> dict[str, object]:
|
|
295
|
+
contract = self.request.finite_variation_contract
|
|
296
|
+
policy_id, policy_version, policy_definition_sha256 = self.policy_identity
|
|
297
|
+
return {
|
|
298
|
+
"schema_version": 1,
|
|
299
|
+
"frame_kind": self.frame_kind.value,
|
|
300
|
+
"request_sha256": self.request.request_sha256,
|
|
301
|
+
"context_sha256": self.request.context_sha256,
|
|
302
|
+
"finite_contract_identity_sha256": contract.identity_sha256,
|
|
303
|
+
"source_forecast_receipt_sha256": (
|
|
304
|
+
self.source_forecast_receipt_sha256
|
|
305
|
+
),
|
|
306
|
+
"parent_receipt_sha256s": list(self.parent_receipt_sha256s),
|
|
307
|
+
"global_row_indices": list(self.global_row_indices),
|
|
308
|
+
"options": [
|
|
309
|
+
{
|
|
310
|
+
"global_row_index": index,
|
|
311
|
+
"option_id": option.option_id,
|
|
312
|
+
"option_identity_sha256": option.identity_sha256,
|
|
313
|
+
"child_configuration_sha256": (
|
|
314
|
+
option.child_configuration_sha256
|
|
315
|
+
),
|
|
316
|
+
"family": option.family,
|
|
317
|
+
}
|
|
318
|
+
for index, option in zip(
|
|
319
|
+
self.global_row_indices,
|
|
320
|
+
(contract.options[value] for value in self.global_row_indices),
|
|
321
|
+
strict=True,
|
|
322
|
+
)
|
|
323
|
+
],
|
|
324
|
+
"forecast_policy": {
|
|
325
|
+
"policy_id": policy_id,
|
|
326
|
+
"policy_version": policy_version,
|
|
327
|
+
"policy_definition_sha256": policy_definition_sha256,
|
|
328
|
+
},
|
|
329
|
+
"partition": (
|
|
330
|
+
None
|
|
331
|
+
if self.block_request is None
|
|
332
|
+
else {
|
|
333
|
+
"layout_sha256": self.block_request.layout.layout_sha256,
|
|
334
|
+
"block_request_sha256": (
|
|
335
|
+
self.block_request.block_request_sha256
|
|
336
|
+
),
|
|
337
|
+
"block_spec_sha256": (
|
|
338
|
+
self.block_request.block.block_spec_sha256
|
|
339
|
+
),
|
|
340
|
+
"block_index": self.block_request.block.block_index,
|
|
341
|
+
"global_row_start": (
|
|
342
|
+
self.block_request.block.global_row_start
|
|
343
|
+
),
|
|
344
|
+
"global_row_stop": self.block_request.block.global_row_stop,
|
|
345
|
+
}
|
|
346
|
+
),
|
|
347
|
+
"subset_policy": (
|
|
348
|
+
None
|
|
349
|
+
if self.subset_policy is None
|
|
350
|
+
else self.subset_policy.to_record()
|
|
351
|
+
),
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
355
|
+
self.__post_init__()
|
|
356
|
+
return self._record_from_validated_values()
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
def receipt_sha256(self) -> str:
|
|
360
|
+
self.__post_init__()
|
|
361
|
+
assert self._validated_receipt_sha256 is not None
|
|
362
|
+
return self._validated_receipt_sha256
|
|
363
|
+
|
|
364
|
+
def to_record(self) -> dict[str, object]:
|
|
365
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
366
|
+
|
|
367
|
+
def __eq__(self, other: object) -> bool:
|
|
368
|
+
return (
|
|
369
|
+
type(self) is ResolvedActionForecastAllocationFrame
|
|
370
|
+
and type(other) is ResolvedActionForecastAllocationFrame
|
|
371
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
__hash__ = None
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def bind_complete_action_forecast_allocation_frame(
|
|
378
|
+
request: ActionForecastRequest,
|
|
379
|
+
forecasts: ResolvedActionForecastBatch,
|
|
380
|
+
*,
|
|
381
|
+
parent_receipt_sha256s: tuple[str, ...] = (),
|
|
382
|
+
) -> ResolvedActionForecastAllocationFrame:
|
|
383
|
+
return ResolvedActionForecastAllocationFrame(
|
|
384
|
+
request=request,
|
|
385
|
+
frame_kind=ActionForecastAllocationFrameKind.COMPLETE,
|
|
386
|
+
global_row_indices=tuple(range(len(request.finite_variation_contract.options))),
|
|
387
|
+
parent_receipt_sha256s=parent_receipt_sha256s,
|
|
388
|
+
complete_batch=forecasts,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def bind_action_forecast_block_allocation_frame(
|
|
393
|
+
block_request: ActionForecastBlockRequest,
|
|
394
|
+
forecasts: ResolvedActionForecastBlock,
|
|
395
|
+
*,
|
|
396
|
+
parent_receipt_sha256s: tuple[str, ...] = (),
|
|
397
|
+
) -> ResolvedActionForecastAllocationFrame:
|
|
398
|
+
spec = block_request.block
|
|
399
|
+
return ResolvedActionForecastAllocationFrame(
|
|
400
|
+
request=block_request.request,
|
|
401
|
+
frame_kind=ActionForecastAllocationFrameKind.PARTITION_BLOCK,
|
|
402
|
+
global_row_indices=tuple(range(spec.global_row_start, spec.global_row_stop)),
|
|
403
|
+
parent_receipt_sha256s=parent_receipt_sha256s,
|
|
404
|
+
block_request=block_request,
|
|
405
|
+
resolved_block=forecasts,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def bind_action_forecast_block_subset_allocation_frame(
|
|
410
|
+
block_request: ActionForecastBlockRequest,
|
|
411
|
+
forecasts: ResolvedActionForecastBlock,
|
|
412
|
+
*,
|
|
413
|
+
included_global_row_indices: tuple[int, ...],
|
|
414
|
+
subset_policy: ActionAllocationFrameSubsetPolicyBinding,
|
|
415
|
+
parent_receipt_sha256s: tuple[str, ...],
|
|
416
|
+
) -> ResolvedActionForecastAllocationFrame:
|
|
417
|
+
return ResolvedActionForecastAllocationFrame(
|
|
418
|
+
request=block_request.request,
|
|
419
|
+
frame_kind=ActionForecastAllocationFrameKind.PARTITION_BLOCK_SUBSET,
|
|
420
|
+
global_row_indices=included_global_row_indices,
|
|
421
|
+
parent_receipt_sha256s=parent_receipt_sha256s,
|
|
422
|
+
block_request=block_request,
|
|
423
|
+
resolved_block=forecasts,
|
|
424
|
+
subset_policy=subset_policy,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
429
|
+
class FrameActionAllocationRequest:
|
|
430
|
+
frame: ResolvedActionForecastAllocationFrame
|
|
431
|
+
eligible_option_ids: tuple[str, ...]
|
|
432
|
+
portfolio_size: int
|
|
433
|
+
utility: ForecastPortfolioUtilityBinding
|
|
434
|
+
_eligible_options_sha256_cache: str | None = field(
|
|
435
|
+
default=None,
|
|
436
|
+
init=False,
|
|
437
|
+
repr=False,
|
|
438
|
+
compare=False,
|
|
439
|
+
)
|
|
440
|
+
_request_sha256_cache: str | None = field(
|
|
441
|
+
default=None,
|
|
442
|
+
init=False,
|
|
443
|
+
repr=False,
|
|
444
|
+
compare=False,
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
def __post_init__(self) -> None:
|
|
448
|
+
if (
|
|
449
|
+
self._eligible_options_sha256_cache is not None
|
|
450
|
+
and self._request_sha256_cache is not None
|
|
451
|
+
):
|
|
452
|
+
require_sha256(
|
|
453
|
+
self._eligible_options_sha256_cache,
|
|
454
|
+
"_eligible_options_sha256_cache",
|
|
455
|
+
)
|
|
456
|
+
require_sha256(
|
|
457
|
+
self._request_sha256_cache,
|
|
458
|
+
"_request_sha256_cache",
|
|
459
|
+
)
|
|
460
|
+
return
|
|
461
|
+
if type(self.frame) is not ResolvedActionForecastAllocationFrame:
|
|
462
|
+
raise TypeError("frame must be an exact allocation frame")
|
|
463
|
+
self.frame.__post_init__()
|
|
464
|
+
if type(self.eligible_option_ids) is not tuple or any(
|
|
465
|
+
type(value) is not str or _OPTION_ID.fullmatch(value) is None
|
|
466
|
+
for value in self.eligible_option_ids
|
|
467
|
+
):
|
|
468
|
+
raise TypeError("eligible_option_ids must be an exact option-ID tuple")
|
|
469
|
+
if not self.eligible_option_ids:
|
|
470
|
+
raise ValueError("eligible_option_ids must be non-empty")
|
|
471
|
+
if self.eligible_option_ids != tuple(sorted(set(self.eligible_option_ids))):
|
|
472
|
+
raise ValueError("eligible option IDs must be unique and canonical")
|
|
473
|
+
available = {value.option_id for value in self.frame.forecasts}
|
|
474
|
+
if not set(self.eligible_option_ids).issubset(available):
|
|
475
|
+
raise ValueError("eligible option IDs escape the authenticated frame")
|
|
476
|
+
if type(self.portfolio_size) is not int or self.portfolio_size <= 0:
|
|
477
|
+
raise ValueError("portfolio_size must be a positive exact integer")
|
|
478
|
+
if self.portfolio_size > len(self.eligible_option_ids):
|
|
479
|
+
raise ValueError("portfolio_size exceeds the eligible frame")
|
|
480
|
+
if type(self.utility) is not ForecastPortfolioUtilityBinding:
|
|
481
|
+
raise TypeError("utility must be an exact identified binding")
|
|
482
|
+
self.utility.__post_init__()
|
|
483
|
+
eligible_options_sha256 = self._eligible_digest_from_validated_values()
|
|
484
|
+
object.__setattr__(
|
|
485
|
+
self,
|
|
486
|
+
"_eligible_options_sha256_cache",
|
|
487
|
+
eligible_options_sha256,
|
|
488
|
+
)
|
|
489
|
+
object.__setattr__(
|
|
490
|
+
self,
|
|
491
|
+
"_request_sha256_cache",
|
|
492
|
+
_hash(
|
|
493
|
+
_REQUEST_DOMAIN,
|
|
494
|
+
self._record_from_validated_values(eligible_options_sha256),
|
|
495
|
+
),
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
def _eligible_digest_from_validated_values(self) -> str:
|
|
499
|
+
by_id = {value.option_id: value for value in self.frame.forecasts}
|
|
500
|
+
return _hash(
|
|
501
|
+
_ELIGIBLE_DOMAIN,
|
|
502
|
+
{
|
|
503
|
+
"frame_receipt_sha256": self.frame.receipt_sha256,
|
|
504
|
+
"options": [
|
|
505
|
+
{
|
|
506
|
+
"option_id": option_id,
|
|
507
|
+
"option_identity_sha256": by_id[
|
|
508
|
+
option_id
|
|
509
|
+
].option_identity_sha256,
|
|
510
|
+
}
|
|
511
|
+
for option_id in self.eligible_option_ids
|
|
512
|
+
],
|
|
513
|
+
},
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
@property
|
|
517
|
+
def eligible_options_sha256(self) -> str:
|
|
518
|
+
self.__post_init__()
|
|
519
|
+
assert self._eligible_options_sha256_cache is not None
|
|
520
|
+
return self._eligible_options_sha256_cache
|
|
521
|
+
|
|
522
|
+
def _record_from_validated_values(
|
|
523
|
+
self,
|
|
524
|
+
eligible_options_sha256: str,
|
|
525
|
+
) -> dict[str, object]:
|
|
526
|
+
by_id = {value.option_id: value for value in self.frame.forecasts}
|
|
527
|
+
return {
|
|
528
|
+
"schema_version": 1,
|
|
529
|
+
"frame_receipt_sha256": self.frame.receipt_sha256,
|
|
530
|
+
"source_forecast_receipt_sha256": (
|
|
531
|
+
self.frame.source_forecast_receipt_sha256
|
|
532
|
+
),
|
|
533
|
+
"eligible_options": [
|
|
534
|
+
{
|
|
535
|
+
"option_id": option_id,
|
|
536
|
+
"option_identity_sha256": by_id[
|
|
537
|
+
option_id
|
|
538
|
+
].option_identity_sha256,
|
|
539
|
+
}
|
|
540
|
+
for option_id in self.eligible_option_ids
|
|
541
|
+
],
|
|
542
|
+
"eligible_options_sha256": eligible_options_sha256,
|
|
543
|
+
"portfolio_size": self.portfolio_size,
|
|
544
|
+
"utility": self.utility.to_record(),
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
548
|
+
self.__post_init__()
|
|
549
|
+
assert self._eligible_options_sha256_cache is not None
|
|
550
|
+
return self._record_from_validated_values(
|
|
551
|
+
self._eligible_options_sha256_cache
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
@property
|
|
555
|
+
def request_sha256(self) -> str:
|
|
556
|
+
self.__post_init__()
|
|
557
|
+
assert self._request_sha256_cache is not None
|
|
558
|
+
return self._request_sha256_cache
|
|
559
|
+
|
|
560
|
+
def to_record(self) -> dict[str, object]:
|
|
561
|
+
return {**self._unsigned_record(), "request_sha256": self.request_sha256}
|
|
562
|
+
|
|
563
|
+
def __eq__(self, other: object) -> bool:
|
|
564
|
+
return (
|
|
565
|
+
type(self) is FrameActionAllocationRequest
|
|
566
|
+
and type(other) is FrameActionAllocationRequest
|
|
567
|
+
and self.request_sha256 == other.request_sha256
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
__hash__ = None
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
574
|
+
class FrameActionPortfolioDecision:
|
|
575
|
+
allocation_request_sha256: str
|
|
576
|
+
frame_receipt_sha256: str
|
|
577
|
+
source_forecast_receipt_sha256: str
|
|
578
|
+
eligible_options_sha256: str
|
|
579
|
+
members: tuple[AllocatedActionMember, ...]
|
|
580
|
+
final_score: PortfolioAllocationScore
|
|
581
|
+
candidate_evaluations: int
|
|
582
|
+
utility_policy_id: str
|
|
583
|
+
utility_policy_version: int
|
|
584
|
+
utility_definition_sha256: str
|
|
585
|
+
allocator_policy_id: str
|
|
586
|
+
allocator_policy_version: int
|
|
587
|
+
allocator_definition_sha256: str
|
|
588
|
+
allocator_configuration_sha256: str
|
|
589
|
+
|
|
590
|
+
def __post_init__(self) -> None:
|
|
591
|
+
for name in (
|
|
592
|
+
"allocation_request_sha256",
|
|
593
|
+
"frame_receipt_sha256",
|
|
594
|
+
"source_forecast_receipt_sha256",
|
|
595
|
+
"eligible_options_sha256",
|
|
596
|
+
"utility_definition_sha256",
|
|
597
|
+
"allocator_definition_sha256",
|
|
598
|
+
"allocator_configuration_sha256",
|
|
599
|
+
):
|
|
600
|
+
require_sha256(getattr(self, name), name)
|
|
601
|
+
if type(self.members) is not tuple or not self.members or any(
|
|
602
|
+
type(value) is not AllocatedActionMember for value in self.members
|
|
603
|
+
):
|
|
604
|
+
raise ValueError("members must be a non-empty exact allocated tuple")
|
|
605
|
+
for value in self.members:
|
|
606
|
+
value.__post_init__()
|
|
607
|
+
if tuple(value.rank for value in self.members) != tuple(
|
|
608
|
+
range(1, len(self.members) + 1)
|
|
609
|
+
):
|
|
610
|
+
raise ValueError("member ranks must be contiguous and ordered")
|
|
611
|
+
if len({value.option_id for value in self.members}) != len(self.members):
|
|
612
|
+
raise ValueError("a frame decision cannot repeat an option")
|
|
613
|
+
if type(self.final_score) is not PortfolioAllocationScore:
|
|
614
|
+
raise TypeError("final_score must be exact PortfolioAllocationScore")
|
|
615
|
+
self.final_score.__post_init__()
|
|
616
|
+
if self.final_score != self.members[-1].greedy_step_score:
|
|
617
|
+
raise ValueError("final_score must equal the last greedy score")
|
|
618
|
+
if type(self.candidate_evaluations) is not int or self.candidate_evaluations <= 0:
|
|
619
|
+
raise ValueError("candidate_evaluations must be positive")
|
|
620
|
+
for prefix in ("utility", "allocator"):
|
|
621
|
+
_token(getattr(self, f"{prefix}_policy_id"), f"{prefix}_policy_id")
|
|
622
|
+
version = getattr(self, f"{prefix}_policy_version")
|
|
623
|
+
if type(version) is not int or version <= 0:
|
|
624
|
+
raise ValueError(f"{prefix}_policy_version must be positive")
|
|
625
|
+
|
|
626
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
627
|
+
self.__post_init__()
|
|
628
|
+
return {
|
|
629
|
+
"schema_version": 1,
|
|
630
|
+
"allocation_request_sha256": self.allocation_request_sha256,
|
|
631
|
+
"frame_receipt_sha256": self.frame_receipt_sha256,
|
|
632
|
+
"source_forecast_receipt_sha256": self.source_forecast_receipt_sha256,
|
|
633
|
+
"eligible_options_sha256": self.eligible_options_sha256,
|
|
634
|
+
"members": [value.to_record() for value in self.members],
|
|
635
|
+
"final_score": self.final_score.to_record(),
|
|
636
|
+
"candidate_evaluations": self.candidate_evaluations,
|
|
637
|
+
"utility_policy": {
|
|
638
|
+
"policy_id": self.utility_policy_id,
|
|
639
|
+
"policy_version": self.utility_policy_version,
|
|
640
|
+
"definition_sha256": self.utility_definition_sha256,
|
|
641
|
+
},
|
|
642
|
+
"allocator_policy": {
|
|
643
|
+
"policy_id": self.allocator_policy_id,
|
|
644
|
+
"policy_version": self.allocator_policy_version,
|
|
645
|
+
"definition_sha256": self.allocator_definition_sha256,
|
|
646
|
+
"configuration_sha256": self.allocator_configuration_sha256,
|
|
647
|
+
},
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
@property
|
|
651
|
+
def receipt_sha256(self) -> str:
|
|
652
|
+
return _hash(_DECISION_DOMAIN, self._unsigned_record())
|
|
653
|
+
|
|
654
|
+
def to_record(self) -> dict[str, object]:
|
|
655
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
656
|
+
|
|
657
|
+
def __eq__(self, other: object) -> bool:
|
|
658
|
+
return (
|
|
659
|
+
type(self) is FrameActionPortfolioDecision
|
|
660
|
+
and type(other) is FrameActionPortfolioDecision
|
|
661
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
__hash__ = None
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def validate_frame_action_portfolio_decision(
|
|
668
|
+
request: FrameActionAllocationRequest,
|
|
669
|
+
decision: FrameActionPortfolioDecision,
|
|
670
|
+
) -> None:
|
|
671
|
+
if type(request) is not FrameActionAllocationRequest:
|
|
672
|
+
raise TypeError("request must be an exact frame allocation request")
|
|
673
|
+
request.__post_init__()
|
|
674
|
+
if type(decision) is not FrameActionPortfolioDecision:
|
|
675
|
+
raise TypeError("decision must be an exact frame portfolio decision")
|
|
676
|
+
decision.__post_init__()
|
|
677
|
+
if (
|
|
678
|
+
decision.allocation_request_sha256 != request.request_sha256
|
|
679
|
+
or decision.frame_receipt_sha256 != request.frame.receipt_sha256
|
|
680
|
+
or decision.source_forecast_receipt_sha256
|
|
681
|
+
!= request.frame.source_forecast_receipt_sha256
|
|
682
|
+
or decision.eligible_options_sha256 != request.eligible_options_sha256
|
|
683
|
+
):
|
|
684
|
+
raise ValueError("frame decision is bound to another request")
|
|
685
|
+
if len(decision.members) != request.portfolio_size:
|
|
686
|
+
raise ValueError("frame decision member count differs from portfolio_size")
|
|
687
|
+
if (
|
|
688
|
+
decision.utility_policy_id != request.utility.policy_id
|
|
689
|
+
or decision.utility_policy_version != request.utility.policy_version
|
|
690
|
+
or decision.utility_definition_sha256 != request.utility.definition_sha256
|
|
691
|
+
):
|
|
692
|
+
raise ValueError("frame decision names another utility policy")
|
|
693
|
+
forecasts = {value.option_id: value for value in request.frame.forecasts}
|
|
694
|
+
for member in decision.members:
|
|
695
|
+
if member.option_id not in request.eligible_option_ids:
|
|
696
|
+
raise ValueError("frame decision selected an ineligible option")
|
|
697
|
+
forecast = forecasts[member.option_id]
|
|
698
|
+
if (
|
|
699
|
+
member.option_identity_sha256 != forecast.option_identity_sha256
|
|
700
|
+
or member.child_configuration_sha256
|
|
701
|
+
!= forecast.child_configuration_sha256
|
|
702
|
+
or member.family != forecast.family
|
|
703
|
+
):
|
|
704
|
+
raise ValueError("frame decision member differs from its forecast")
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
@dataclass(frozen=True, slots=True)
|
|
708
|
+
class AllocationCandidateScoreDiagnosticInput:
|
|
709
|
+
allocation_request_sha256: str
|
|
710
|
+
step: int
|
|
711
|
+
candidate_label: str
|
|
712
|
+
members: tuple[ResolvedActionForecast, ...]
|
|
713
|
+
score: PortfolioAllocationScore
|
|
714
|
+
marginal_total_utility: float
|
|
715
|
+
|
|
716
|
+
def __post_init__(self) -> None:
|
|
717
|
+
require_sha256(self.allocation_request_sha256, "allocation_request_sha256")
|
|
718
|
+
if type(self.step) is not int or self.step <= 0:
|
|
719
|
+
raise ValueError("step must be a positive exact integer")
|
|
720
|
+
if type(self.candidate_label) is not str or _CANDIDATE_LABEL.fullmatch(
|
|
721
|
+
self.candidate_label
|
|
722
|
+
) is None:
|
|
723
|
+
raise ValueError("candidate_label must be an opaque global-row label")
|
|
724
|
+
if type(self.members) is not tuple or not self.members or any(
|
|
725
|
+
type(value) is not ResolvedActionForecast for value in self.members
|
|
726
|
+
):
|
|
727
|
+
raise ValueError("members must be a non-empty resolved forecast tuple")
|
|
728
|
+
for value in self.members:
|
|
729
|
+
value.__post_init__()
|
|
730
|
+
if type(self.score) is not PortfolioAllocationScore:
|
|
731
|
+
raise TypeError("score must be exact PortfolioAllocationScore")
|
|
732
|
+
self.score.__post_init__()
|
|
733
|
+
_finite_float(self.marginal_total_utility, "marginal_total_utility")
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
@dataclass(frozen=True, slots=True)
|
|
737
|
+
class AllocationCandidateScoreDiagnostic:
|
|
738
|
+
boundary_or_extreme: bool
|
|
739
|
+
|
|
740
|
+
def __post_init__(self) -> None:
|
|
741
|
+
if type(self.boundary_or_extreme) is not bool:
|
|
742
|
+
raise TypeError("boundary_or_extreme must be an exact bool")
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
@runtime_checkable
|
|
746
|
+
class AllocationScoreDiagnostic(Protocol):
|
|
747
|
+
def __call__(
|
|
748
|
+
self,
|
|
749
|
+
request: AllocationCandidateScoreDiagnosticInput,
|
|
750
|
+
) -> AllocationCandidateScoreDiagnostic: ...
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
@dataclass(frozen=True, slots=True)
|
|
754
|
+
class AllocationScoreDiagnosticBinding:
|
|
755
|
+
diagnostic: AllocationScoreDiagnostic = field(repr=False, compare=False)
|
|
756
|
+
policy_id: str
|
|
757
|
+
policy_version: int
|
|
758
|
+
policy_definition_sha256: str
|
|
759
|
+
|
|
760
|
+
def __post_init__(self) -> None:
|
|
761
|
+
if not callable(self.diagnostic):
|
|
762
|
+
raise TypeError("diagnostic must be callable")
|
|
763
|
+
_token(self.policy_id, "policy_id")
|
|
764
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
765
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
766
|
+
require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
|
|
767
|
+
|
|
768
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
769
|
+
self.__post_init__()
|
|
770
|
+
return {
|
|
771
|
+
"schema_version": 1,
|
|
772
|
+
"policy_id": self.policy_id,
|
|
773
|
+
"policy_version": self.policy_version,
|
|
774
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
@property
|
|
778
|
+
def binding_sha256(self) -> str:
|
|
779
|
+
return _hash(_DIAGNOSTIC_POLICY_DOMAIN, self._unsigned_record())
|
|
780
|
+
|
|
781
|
+
def to_record(self) -> dict[str, object]:
|
|
782
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
786
|
+
class AllocationSurfaceGatePolicyBinding:
|
|
787
|
+
"""Domain-neutral thresholds applied before evaluator authority opens."""
|
|
788
|
+
|
|
789
|
+
policy_id: str
|
|
790
|
+
policy_version: int
|
|
791
|
+
policy_definition_sha256: str
|
|
792
|
+
minimum_distinct_finite_scores: int
|
|
793
|
+
maximum_top_tie_share: float
|
|
794
|
+
maximum_boundary_or_extreme_share: float
|
|
795
|
+
minimum_winner_runner_gap: float
|
|
796
|
+
|
|
797
|
+
def __post_init__(self) -> None:
|
|
798
|
+
_token(self.policy_id, "policy_id")
|
|
799
|
+
if type(self.policy_version) is not int or self.policy_version <= 0:
|
|
800
|
+
raise ValueError("policy_version must be a positive exact integer")
|
|
801
|
+
require_sha256(self.policy_definition_sha256, "policy_definition_sha256")
|
|
802
|
+
if (
|
|
803
|
+
type(self.minimum_distinct_finite_scores) is not int
|
|
804
|
+
or self.minimum_distinct_finite_scores <= 0
|
|
805
|
+
):
|
|
806
|
+
raise ValueError("minimum_distinct_finite_scores must be positive")
|
|
807
|
+
for name in (
|
|
808
|
+
"maximum_top_tie_share",
|
|
809
|
+
"maximum_boundary_or_extreme_share",
|
|
810
|
+
):
|
|
811
|
+
value = _finite_float(getattr(self, name), name)
|
|
812
|
+
if not 0.0 <= value <= 1.0:
|
|
813
|
+
raise ValueError(f"{name} must lie in [0,1]")
|
|
814
|
+
_finite_float(self.minimum_winner_runner_gap, "minimum_winner_runner_gap")
|
|
815
|
+
if self.minimum_winner_runner_gap < 0.0:
|
|
816
|
+
raise ValueError("minimum_winner_runner_gap cannot be negative")
|
|
817
|
+
|
|
818
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
819
|
+
self.__post_init__()
|
|
820
|
+
return {
|
|
821
|
+
"schema_version": 1,
|
|
822
|
+
"policy_id": self.policy_id,
|
|
823
|
+
"policy_version": self.policy_version,
|
|
824
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
825
|
+
"minimum_distinct_finite_scores": (
|
|
826
|
+
self.minimum_distinct_finite_scores
|
|
827
|
+
),
|
|
828
|
+
"maximum_top_tie_share_hex": self.maximum_top_tie_share.hex(),
|
|
829
|
+
"maximum_boundary_or_extreme_share_hex": (
|
|
830
|
+
self.maximum_boundary_or_extreme_share.hex()
|
|
831
|
+
),
|
|
832
|
+
"minimum_winner_runner_gap_hex": (
|
|
833
|
+
self.minimum_winner_runner_gap.hex()
|
|
834
|
+
),
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
@property
|
|
838
|
+
def binding_sha256(self) -> str:
|
|
839
|
+
return _hash(_GATE_POLICY_DOMAIN, self._unsigned_record())
|
|
840
|
+
|
|
841
|
+
def to_record(self) -> dict[str, object]:
|
|
842
|
+
return {**self._unsigned_record(), "binding_sha256": self.binding_sha256}
|
|
843
|
+
|
|
844
|
+
def __eq__(self, other: object) -> bool:
|
|
845
|
+
return (
|
|
846
|
+
type(self) is AllocationSurfaceGatePolicyBinding
|
|
847
|
+
and type(other) is AllocationSurfaceGatePolicyBinding
|
|
848
|
+
and self.binding_sha256 == other.binding_sha256
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
__hash__ = None
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
def allocation_score_multiset_sha256(scores: tuple[float, ...]) -> str:
|
|
855
|
+
if type(scores) is not tuple or not scores:
|
|
856
|
+
raise ValueError("scores must be a non-empty exact tuple")
|
|
857
|
+
for index, score in enumerate(scores):
|
|
858
|
+
_finite_float(score, f"scores[{index}]")
|
|
859
|
+
return _hash(
|
|
860
|
+
_SCORE_MULTISET_DOMAIN,
|
|
861
|
+
{"score_hex_multiset": sorted(value.hex() for value in scores)},
|
|
862
|
+
)
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
866
|
+
class AllocationSurfaceStepAudit:
|
|
867
|
+
step: int
|
|
868
|
+
candidate_count: int
|
|
869
|
+
distinct_finite_score_count: int
|
|
870
|
+
top_tie_count: int
|
|
871
|
+
winner_runner_gap: float
|
|
872
|
+
boundary_or_extreme_count: int
|
|
873
|
+
boundary_or_extreme_share: float
|
|
874
|
+
score_multiset_sha256: str
|
|
875
|
+
winner_candidate_label: str
|
|
876
|
+
tie_break_used: bool
|
|
877
|
+
failure_codes: tuple[str, ...]
|
|
878
|
+
passes: bool
|
|
879
|
+
|
|
880
|
+
def __post_init__(self) -> None:
|
|
881
|
+
if type(self.step) is not int or self.step <= 0:
|
|
882
|
+
raise ValueError("step must be a positive exact integer")
|
|
883
|
+
for name in (
|
|
884
|
+
"candidate_count",
|
|
885
|
+
"distinct_finite_score_count",
|
|
886
|
+
"top_tie_count",
|
|
887
|
+
):
|
|
888
|
+
value = getattr(self, name)
|
|
889
|
+
if type(value) is not int or value <= 0:
|
|
890
|
+
raise ValueError(f"{name} must be a positive exact integer")
|
|
891
|
+
if value > self.candidate_count:
|
|
892
|
+
raise ValueError(f"{name} cannot exceed candidate_count")
|
|
893
|
+
if (
|
|
894
|
+
type(self.boundary_or_extreme_count) is not int
|
|
895
|
+
or not 0 <= self.boundary_or_extreme_count <= self.candidate_count
|
|
896
|
+
):
|
|
897
|
+
raise ValueError("boundary_or_extreme_count is outside candidate_count")
|
|
898
|
+
_finite_float(self.winner_runner_gap, "winner_runner_gap")
|
|
899
|
+
if self.winner_runner_gap < 0.0:
|
|
900
|
+
raise ValueError("winner_runner_gap cannot be negative")
|
|
901
|
+
_finite_float(
|
|
902
|
+
self.boundary_or_extreme_share,
|
|
903
|
+
"boundary_or_extreme_share",
|
|
904
|
+
)
|
|
905
|
+
if not 0.0 <= self.boundary_or_extreme_share <= 1.0:
|
|
906
|
+
raise ValueError("boundary_or_extreme_share must lie in [0,1]")
|
|
907
|
+
expected_share = self.boundary_or_extreme_count / self.candidate_count
|
|
908
|
+
if self.boundary_or_extreme_share != expected_share:
|
|
909
|
+
raise ValueError("boundary/extreme share differs from its count")
|
|
910
|
+
require_sha256(self.score_multiset_sha256, "score_multiset_sha256")
|
|
911
|
+
if type(self.winner_candidate_label) is not str or _CANDIDATE_LABEL.fullmatch(
|
|
912
|
+
self.winner_candidate_label
|
|
913
|
+
) is None:
|
|
914
|
+
raise ValueError("winner_candidate_label is not an opaque row label")
|
|
915
|
+
if type(self.tie_break_used) is not bool or type(self.passes) is not bool:
|
|
916
|
+
raise TypeError("tie_break_used and passes must be exact booleans")
|
|
917
|
+
if self.tie_break_used is not (self.top_tie_count > 1):
|
|
918
|
+
raise ValueError("tie_break_used differs from top_tie_count")
|
|
919
|
+
if type(self.failure_codes) is not tuple or any(
|
|
920
|
+
type(value) is not str or _TOKEN.fullmatch(value) is None
|
|
921
|
+
for value in self.failure_codes
|
|
922
|
+
):
|
|
923
|
+
raise TypeError("failure_codes must be an exact token tuple")
|
|
924
|
+
if self.failure_codes != tuple(sorted(set(self.failure_codes))):
|
|
925
|
+
raise ValueError("failure_codes must be unique and canonical")
|
|
926
|
+
if self.passes is not (not self.failure_codes):
|
|
927
|
+
raise ValueError("passes differs from failure_codes")
|
|
928
|
+
|
|
929
|
+
@property
|
|
930
|
+
def top_tie_share(self) -> float:
|
|
931
|
+
return self.top_tie_count / self.candidate_count
|
|
932
|
+
|
|
933
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
934
|
+
self.__post_init__()
|
|
935
|
+
return {
|
|
936
|
+
"schema_version": 1,
|
|
937
|
+
"step": self.step,
|
|
938
|
+
"candidate_count": self.candidate_count,
|
|
939
|
+
"score_kind": "marginal_total_utility",
|
|
940
|
+
"distinct_finite_score_count": self.distinct_finite_score_count,
|
|
941
|
+
"top_tie_count": self.top_tie_count,
|
|
942
|
+
"top_tie_share_hex": self.top_tie_share.hex(),
|
|
943
|
+
"winner_runner_gap_hex": self.winner_runner_gap.hex(),
|
|
944
|
+
"boundary_or_extreme_count": self.boundary_or_extreme_count,
|
|
945
|
+
"boundary_or_extreme_share_hex": (
|
|
946
|
+
self.boundary_or_extreme_share.hex()
|
|
947
|
+
),
|
|
948
|
+
"score_multiset_sha256": self.score_multiset_sha256,
|
|
949
|
+
"winner_candidate_label": self.winner_candidate_label,
|
|
950
|
+
"tie_break_used": self.tie_break_used,
|
|
951
|
+
"failure_codes": list(self.failure_codes),
|
|
952
|
+
"passes": self.passes,
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
@property
|
|
956
|
+
def receipt_sha256(self) -> str:
|
|
957
|
+
return _hash(_STEP_AUDIT_DOMAIN, self._unsigned_record())
|
|
958
|
+
|
|
959
|
+
def to_record(self) -> dict[str, object]:
|
|
960
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
961
|
+
|
|
962
|
+
def __eq__(self, other: object) -> bool:
|
|
963
|
+
return (
|
|
964
|
+
type(self) is AllocationSurfaceStepAudit
|
|
965
|
+
and type(other) is AllocationSurfaceStepAudit
|
|
966
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
__hash__ = None
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
def allocation_surface_failure_codes(
|
|
973
|
+
*,
|
|
974
|
+
policy: AllocationSurfaceGatePolicyBinding,
|
|
975
|
+
candidate_count: int,
|
|
976
|
+
distinct_finite_score_count: int,
|
|
977
|
+
top_tie_count: int,
|
|
978
|
+
winner_runner_gap: float,
|
|
979
|
+
boundary_or_extreme_share: float,
|
|
980
|
+
) -> tuple[str, ...]:
|
|
981
|
+
policy.__post_init__()
|
|
982
|
+
failures: list[str] = []
|
|
983
|
+
if distinct_finite_score_count < policy.minimum_distinct_finite_scores:
|
|
984
|
+
failures.append("insufficient_distinct_scores")
|
|
985
|
+
if top_tie_count / candidate_count > policy.maximum_top_tie_share:
|
|
986
|
+
failures.append("top_tie_concentration")
|
|
987
|
+
if (
|
|
988
|
+
boundary_or_extreme_share
|
|
989
|
+
> policy.maximum_boundary_or_extreme_share
|
|
990
|
+
):
|
|
991
|
+
failures.append("boundary_extreme_concentration")
|
|
992
|
+
if candidate_count > 1 and winner_runner_gap < policy.minimum_winner_runner_gap:
|
|
993
|
+
failures.append("winner_runner_gap_too_small")
|
|
994
|
+
return tuple(sorted(failures))
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
998
|
+
class ActionAllocationSurfaceAudit:
|
|
999
|
+
allocation_request_sha256: str
|
|
1000
|
+
decision_receipt_sha256: str
|
|
1001
|
+
frame_receipt_sha256: str
|
|
1002
|
+
score_diagnostic: AllocationScoreDiagnosticBinding
|
|
1003
|
+
gate_policy: AllocationSurfaceGatePolicyBinding
|
|
1004
|
+
steps: tuple[AllocationSurfaceStepAudit, ...]
|
|
1005
|
+
candidate_score_count: int
|
|
1006
|
+
passes: bool
|
|
1007
|
+
|
|
1008
|
+
def __post_init__(self) -> None:
|
|
1009
|
+
for name in (
|
|
1010
|
+
"allocation_request_sha256",
|
|
1011
|
+
"decision_receipt_sha256",
|
|
1012
|
+
"frame_receipt_sha256",
|
|
1013
|
+
):
|
|
1014
|
+
require_sha256(getattr(self, name), name)
|
|
1015
|
+
if type(self.score_diagnostic) is not AllocationScoreDiagnosticBinding:
|
|
1016
|
+
raise TypeError("score_diagnostic must be an exact binding")
|
|
1017
|
+
self.score_diagnostic.__post_init__()
|
|
1018
|
+
if type(self.gate_policy) is not AllocationSurfaceGatePolicyBinding:
|
|
1019
|
+
raise TypeError("gate_policy must be an exact binding")
|
|
1020
|
+
self.gate_policy.__post_init__()
|
|
1021
|
+
if type(self.steps) is not tuple or not self.steps or any(
|
|
1022
|
+
type(value) is not AllocationSurfaceStepAudit for value in self.steps
|
|
1023
|
+
):
|
|
1024
|
+
raise ValueError("steps must be a non-empty exact audit tuple")
|
|
1025
|
+
for value in self.steps:
|
|
1026
|
+
value.__post_init__()
|
|
1027
|
+
if tuple(value.step for value in self.steps) != tuple(
|
|
1028
|
+
range(1, len(self.steps) + 1)
|
|
1029
|
+
):
|
|
1030
|
+
raise ValueError("audit steps must be contiguous and ordered")
|
|
1031
|
+
for step in self.steps:
|
|
1032
|
+
expected_failures = allocation_surface_failure_codes(
|
|
1033
|
+
policy=self.gate_policy,
|
|
1034
|
+
candidate_count=step.candidate_count,
|
|
1035
|
+
distinct_finite_score_count=step.distinct_finite_score_count,
|
|
1036
|
+
top_tie_count=step.top_tie_count,
|
|
1037
|
+
winner_runner_gap=step.winner_runner_gap,
|
|
1038
|
+
boundary_or_extreme_share=step.boundary_or_extreme_share,
|
|
1039
|
+
)
|
|
1040
|
+
if step.failure_codes != expected_failures:
|
|
1041
|
+
raise ValueError("step failure codes differ from the gate policy")
|
|
1042
|
+
expected_count = sum(value.candidate_count for value in self.steps)
|
|
1043
|
+
if (
|
|
1044
|
+
type(self.candidate_score_count) is not int
|
|
1045
|
+
or self.candidate_score_count != expected_count
|
|
1046
|
+
):
|
|
1047
|
+
raise ValueError("candidate_score_count differs from audit steps")
|
|
1048
|
+
if type(self.passes) is not bool:
|
|
1049
|
+
raise TypeError("passes must be an exact bool")
|
|
1050
|
+
if self.passes is not all(value.passes for value in self.steps):
|
|
1051
|
+
raise ValueError("audit passes differs from its step gates")
|
|
1052
|
+
|
|
1053
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1054
|
+
self.__post_init__()
|
|
1055
|
+
return {
|
|
1056
|
+
"schema_version": 1,
|
|
1057
|
+
"allocation_request_sha256": self.allocation_request_sha256,
|
|
1058
|
+
"decision_receipt_sha256": self.decision_receipt_sha256,
|
|
1059
|
+
"frame_receipt_sha256": self.frame_receipt_sha256,
|
|
1060
|
+
"score_diagnostic": self.score_diagnostic.to_record(),
|
|
1061
|
+
"gate_policy": self.gate_policy.to_record(),
|
|
1062
|
+
"steps": [value.to_record() for value in self.steps],
|
|
1063
|
+
"candidate_score_count": self.candidate_score_count,
|
|
1064
|
+
"passes": self.passes,
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
@property
|
|
1068
|
+
def receipt_sha256(self) -> str:
|
|
1069
|
+
return _hash(_SURFACE_AUDIT_DOMAIN, self._unsigned_record())
|
|
1070
|
+
|
|
1071
|
+
def to_record(self) -> dict[str, object]:
|
|
1072
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
1073
|
+
|
|
1074
|
+
def __eq__(self, other: object) -> bool:
|
|
1075
|
+
return (
|
|
1076
|
+
type(self) is ActionAllocationSurfaceAudit
|
|
1077
|
+
and type(other) is ActionAllocationSurfaceAudit
|
|
1078
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
__hash__ = None
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
1085
|
+
class AuditedFrameActionAllocationResult:
|
|
1086
|
+
decision: FrameActionPortfolioDecision
|
|
1087
|
+
audit: ActionAllocationSurfaceAudit
|
|
1088
|
+
|
|
1089
|
+
def __post_init__(self) -> None:
|
|
1090
|
+
if type(self.decision) is not FrameActionPortfolioDecision:
|
|
1091
|
+
raise TypeError("decision must be an exact frame decision")
|
|
1092
|
+
self.decision.__post_init__()
|
|
1093
|
+
if type(self.audit) is not ActionAllocationSurfaceAudit:
|
|
1094
|
+
raise TypeError("audit must be an exact allocation-surface audit")
|
|
1095
|
+
self.audit.__post_init__()
|
|
1096
|
+
if self.audit.decision_receipt_sha256 != self.decision.receipt_sha256:
|
|
1097
|
+
raise ValueError("audit is bound to another decision")
|
|
1098
|
+
if self.audit.allocation_request_sha256 != (
|
|
1099
|
+
self.decision.allocation_request_sha256
|
|
1100
|
+
):
|
|
1101
|
+
raise ValueError("audit and decision name different requests")
|
|
1102
|
+
if self.audit.frame_receipt_sha256 != self.decision.frame_receipt_sha256:
|
|
1103
|
+
raise ValueError("audit and decision name different frames")
|
|
1104
|
+
if self.audit.candidate_score_count != self.decision.candidate_evaluations:
|
|
1105
|
+
raise ValueError("audit and decision candidate counts differ")
|
|
1106
|
+
|
|
1107
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1108
|
+
self.__post_init__()
|
|
1109
|
+
return {
|
|
1110
|
+
"schema_version": 1,
|
|
1111
|
+
"decision_receipt_sha256": self.decision.receipt_sha256,
|
|
1112
|
+
"audit_receipt_sha256": self.audit.receipt_sha256,
|
|
1113
|
+
"authorized": self.audit.passes,
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
@property
|
|
1117
|
+
def receipt_sha256(self) -> str:
|
|
1118
|
+
return _hash(_RESULT_DOMAIN, self._unsigned_record())
|
|
1119
|
+
|
|
1120
|
+
def to_record(self) -> dict[str, object]:
|
|
1121
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
1122
|
+
|
|
1123
|
+
def __eq__(self, other: object) -> bool:
|
|
1124
|
+
return (
|
|
1125
|
+
type(self) is AuditedFrameActionAllocationResult
|
|
1126
|
+
and type(other) is AuditedFrameActionAllocationResult
|
|
1127
|
+
and self.receipt_sha256 == other.receipt_sha256
|
|
1128
|
+
)
|
|
1129
|
+
|
|
1130
|
+
__hash__ = None
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
__all__ = [
|
|
1134
|
+
"ActionAllocationFrameSubsetPolicyBinding",
|
|
1135
|
+
"ActionAllocationSurfaceAudit",
|
|
1136
|
+
"ActionForecastAllocationFrameKind",
|
|
1137
|
+
"AllocationCandidateScoreDiagnostic",
|
|
1138
|
+
"AllocationCandidateScoreDiagnosticInput",
|
|
1139
|
+
"AllocationScoreDiagnostic",
|
|
1140
|
+
"AllocationScoreDiagnosticBinding",
|
|
1141
|
+
"AllocationSurfaceGatePolicyBinding",
|
|
1142
|
+
"AllocationSurfaceStepAudit",
|
|
1143
|
+
"AuditedFrameActionAllocationResult",
|
|
1144
|
+
"FrameActionAllocationRequest",
|
|
1145
|
+
"FrameActionPortfolioDecision",
|
|
1146
|
+
"ResolvedActionForecastAllocationFrame",
|
|
1147
|
+
"allocation_score_multiset_sha256",
|
|
1148
|
+
"allocation_surface_failure_codes",
|
|
1149
|
+
"bind_action_forecast_block_allocation_frame",
|
|
1150
|
+
"bind_action_forecast_block_subset_allocation_frame",
|
|
1151
|
+
"bind_complete_action_forecast_allocation_frame",
|
|
1152
|
+
"validate_frame_action_portfolio_decision",
|
|
1153
|
+
]
|