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,1977 @@
|
|
|
1
|
+
"""Transactional runtime adapter for closed-loop campaign insight learning.
|
|
2
|
+
|
|
3
|
+
This module is the workload-neutral bridge between campaign runtime hooks and
|
|
4
|
+
``ClosedLoopCampaignLearning``. Workload adapters project opaque reflection
|
|
5
|
+
results into typed drafts/evidence/audit-plan templates. The concrete
|
|
6
|
+
transactional generation auditor then registers evaluated finite actions and
|
|
7
|
+
issues global audit bindings through the sealed falsification gate. The bridge
|
|
8
|
+
owns memory registration, bank-issued diagnostic admissions, pure lifecycle
|
|
9
|
+
preparation, and post-memory commit; it never interprets benchmark metrics.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import hashlib
|
|
15
|
+
import json
|
|
16
|
+
import math
|
|
17
|
+
import re
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from typing import Protocol, runtime_checkable
|
|
20
|
+
|
|
21
|
+
from agent_evolve.application.campaign_execution import (
|
|
22
|
+
CampaignReflectionReceipt,
|
|
23
|
+
CampaignReflectionRequest,
|
|
24
|
+
CampaignReflectionStatus,
|
|
25
|
+
CampaignReflectionTestAdmissionRequest,
|
|
26
|
+
CampaignStageRequest,
|
|
27
|
+
)
|
|
28
|
+
from agent_evolve.application.campaign_generation_audit import (
|
|
29
|
+
CampaignGenerationAuditPreparation,
|
|
30
|
+
CampaignGenerationAuditProjection,
|
|
31
|
+
TransactionalPortfolioGenerationAuditor,
|
|
32
|
+
)
|
|
33
|
+
from agent_evolve.application.campaign_learning import (
|
|
34
|
+
CampaignDiagnosticAdmissionReceipt,
|
|
35
|
+
CampaignInsightRegistrationReceipt,
|
|
36
|
+
CampaignPreparedLearningBarrier,
|
|
37
|
+
CampaignSemanticAuditPlan,
|
|
38
|
+
ClosedLoopCampaignLearning,
|
|
39
|
+
)
|
|
40
|
+
from agent_evolve.application.insight_memory import (
|
|
41
|
+
EmpiricalEvidenceSnapshot,
|
|
42
|
+
InsightEvidenceLineage,
|
|
43
|
+
InsightLifecycleState,
|
|
44
|
+
InsightMemoryEntry,
|
|
45
|
+
QuarantineTestAdmissionReceipt,
|
|
46
|
+
ReflectedInsightBatchItem,
|
|
47
|
+
)
|
|
48
|
+
from agent_evolve.domain.finite_variation import FiniteActionEvidenceBinding
|
|
49
|
+
from agent_evolve.domain.ids import CandidateId, LLMCallId, OperatorInvocationId
|
|
50
|
+
from agent_evolve.application.portfolio_evolution import (
|
|
51
|
+
PortfolioMemoryCreditBatchPreparation,
|
|
52
|
+
PortfolioVariationWaveRequest,
|
|
53
|
+
PortfolioVariationWaveResult,
|
|
54
|
+
)
|
|
55
|
+
from agent_evolve.application.portfolio_campaign_runtime import (
|
|
56
|
+
CampaignPortfolioLearningPreparation,
|
|
57
|
+
)
|
|
58
|
+
from agent_evolve.domain.insight import InsightRef
|
|
59
|
+
from agent_evolve.domain.patch import require_sha256
|
|
60
|
+
from agent_evolve.domain.typed_json import (
|
|
61
|
+
FrozenJsonObject,
|
|
62
|
+
freeze_json,
|
|
63
|
+
thaw_json,
|
|
64
|
+
typed_json_sha256,
|
|
65
|
+
)
|
|
66
|
+
from agent_evolve.policies.memory.global_falsification import (
|
|
67
|
+
GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256,
|
|
68
|
+
HypothesisAuditScope,
|
|
69
|
+
HypothesisClaimStrength,
|
|
70
|
+
HypothesisMetricPrediction,
|
|
71
|
+
TypedEvidencePredicate,
|
|
72
|
+
TypedInterventionSignature,
|
|
73
|
+
)
|
|
74
|
+
from agent_evolve.ports.agentic_generator import (
|
|
75
|
+
InsightDraft,
|
|
76
|
+
MetricComparisonAnchor,
|
|
77
|
+
MetricComparisonAnchorKind,
|
|
78
|
+
MetricEffectDirection,
|
|
79
|
+
MetricEffectPrediction,
|
|
80
|
+
ReflectionConsumerScope,
|
|
81
|
+
ReflectionEvidenceCatalog,
|
|
82
|
+
ReflectionEvidenceCatalogEntry,
|
|
83
|
+
ReflectionInsightContract,
|
|
84
|
+
ReflectionInsightKind,
|
|
85
|
+
validate_reflection_insight_draft,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
|
|
90
|
+
_OPERATOR = re.compile(r"^[a-z][a-z0-9_]*$")
|
|
91
|
+
_PATH = re.compile(r"^\$\.[^.\[\]\s]+(?:\.[^.\[\]\s]+|\[(?:0|[1-9][0-9]*)\])*$")
|
|
92
|
+
_TEMPLATE_DOMAIN = b"agent-evolve:campaign-semantic-audit-template:v1\x00"
|
|
93
|
+
_REFLECTION_PROJECTION_DOMAIN = (
|
|
94
|
+
b"agent-evolve:campaign-reflection-learning-projection:v1\x00"
|
|
95
|
+
)
|
|
96
|
+
_REFLECTION_REGISTRATION_DOMAIN = (
|
|
97
|
+
b"agent-evolve:campaign-runtime-reflection-registration:v1\x00"
|
|
98
|
+
)
|
|
99
|
+
_DIAGNOSTIC_EXPOSURE_DOMAIN = (
|
|
100
|
+
b"agent-evolve:campaign-runtime-diagnostic-exposure:v1\x00"
|
|
101
|
+
)
|
|
102
|
+
_REFLECTION_LEARNING_RECORD_DOMAIN = (
|
|
103
|
+
b"agent-evolve:campaign-reflection-learning-record:v1\x00"
|
|
104
|
+
)
|
|
105
|
+
_COMPILED_INSIGHT_SEMANTICS_DOMAIN = (
|
|
106
|
+
b"agent-evolve:campaign-compiled-insight-semantics:v1\x00"
|
|
107
|
+
)
|
|
108
|
+
CAMPAIGN_REFLECTION_LEARNING_RECORD_KEY = "campaign_reflection_learning"
|
|
109
|
+
STRUCTURED_REFLECTION_PROJECTION_POLICY_ID = "structured_reflection_learning"
|
|
110
|
+
STRUCTURED_REFLECTION_PROJECTION_POLICY_VERSION = 1
|
|
111
|
+
STRUCTURED_REFLECTION_PROJECTION_DEFINITION_SHA256 = hashlib.sha256(
|
|
112
|
+
b"agent-evolve:structured-reflection-learning-projection:v1"
|
|
113
|
+
).hexdigest()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _canonical_json(value: object) -> bytes:
|
|
117
|
+
return json.dumps(
|
|
118
|
+
value,
|
|
119
|
+
allow_nan=False,
|
|
120
|
+
ensure_ascii=True,
|
|
121
|
+
separators=(",", ":"),
|
|
122
|
+
sort_keys=True,
|
|
123
|
+
).encode("ascii", errors="strict")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
127
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _object(value: dict[str, object]) -> FrozenJsonObject:
|
|
131
|
+
frozen = freeze_json(value)
|
|
132
|
+
if type(frozen) is not FrozenJsonObject: # pragma: no cover - closed root.
|
|
133
|
+
raise AssertionError("runtime learning evidence did not freeze to an object")
|
|
134
|
+
return frozen
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _reference_record(reference: InsightRef) -> dict[str, object]:
|
|
138
|
+
return {
|
|
139
|
+
"insight_id": reference.insight_id.value,
|
|
140
|
+
"version": reference.version,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _canonical_tokens(
|
|
145
|
+
values: tuple[str, ...],
|
|
146
|
+
*,
|
|
147
|
+
name: str,
|
|
148
|
+
pattern: re.Pattern[str],
|
|
149
|
+
allow_empty: bool = False,
|
|
150
|
+
) -> None:
|
|
151
|
+
if type(values) is not tuple or any(
|
|
152
|
+
type(value) is not str or pattern.fullmatch(value) is None for value in values
|
|
153
|
+
):
|
|
154
|
+
raise TypeError(f"{name} must contain canonical strings")
|
|
155
|
+
if not allow_empty and not values:
|
|
156
|
+
raise ValueError(f"{name} cannot be empty")
|
|
157
|
+
if values != tuple(sorted(set(values))):
|
|
158
|
+
raise ValueError(f"{name} must be unique and canonical")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _paths_overlap(first: str, second: str) -> bool:
|
|
162
|
+
return (
|
|
163
|
+
first == second
|
|
164
|
+
or first.startswith(second + ".")
|
|
165
|
+
or first.startswith(second + "[")
|
|
166
|
+
or second.startswith(first + ".")
|
|
167
|
+
or second.startswith(first + "[")
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _plain_object(value: object, *, name: str) -> dict[str, object]:
|
|
172
|
+
if type(value) is not dict:
|
|
173
|
+
raise TypeError(f"{name} must be an exact JSON object")
|
|
174
|
+
return value
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _exact_keys(
|
|
178
|
+
value: dict[str, object],
|
|
179
|
+
expected: set[str],
|
|
180
|
+
*,
|
|
181
|
+
name: str,
|
|
182
|
+
) -> None:
|
|
183
|
+
observed = set(value)
|
|
184
|
+
if observed != expected:
|
|
185
|
+
missing = tuple(sorted(expected - observed))
|
|
186
|
+
foreign = tuple(sorted(observed - expected))
|
|
187
|
+
raise ValueError(
|
|
188
|
+
f"{name} fields differ from the canonical schema; "
|
|
189
|
+
f"missing={missing!r}, foreign={foreign!r}"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _string_tuple(value: object, *, name: str) -> tuple[str, ...]:
|
|
194
|
+
if type(value) is not list or any(type(item) is not str for item in value):
|
|
195
|
+
raise TypeError(f"{name} must be an exact JSON string array")
|
|
196
|
+
return tuple(value)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _object_tuple(value: object, *, name: str) -> tuple[dict[str, object], ...]:
|
|
200
|
+
if type(value) is not list or any(type(item) is not dict for item in value):
|
|
201
|
+
raise TypeError(f"{name} must be an exact JSON object array")
|
|
202
|
+
return tuple(value)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _decode_metric_prediction(
|
|
206
|
+
value: object,
|
|
207
|
+
*,
|
|
208
|
+
name: str,
|
|
209
|
+
) -> MetricEffectPrediction:
|
|
210
|
+
record = _plain_object(value, name=name)
|
|
211
|
+
allowed = {"metric_id", "direction", "comparison_anchor"}
|
|
212
|
+
if set(record) not in (
|
|
213
|
+
{"metric_id", "direction"},
|
|
214
|
+
allowed,
|
|
215
|
+
):
|
|
216
|
+
_exact_keys(record, allowed, name=name)
|
|
217
|
+
metric_id = record.get("metric_id")
|
|
218
|
+
direction = record.get("direction")
|
|
219
|
+
if type(metric_id) is not str or type(direction) is not str:
|
|
220
|
+
raise TypeError(f"{name} metric_id and direction must be exact strings")
|
|
221
|
+
anchor_record = record.get("comparison_anchor")
|
|
222
|
+
anchor: MetricComparisonAnchor | None
|
|
223
|
+
if anchor_record is None:
|
|
224
|
+
anchor = None
|
|
225
|
+
else:
|
|
226
|
+
raw_anchor = _plain_object(anchor_record, name=f"{name}.comparison_anchor")
|
|
227
|
+
_exact_keys(
|
|
228
|
+
raw_anchor,
|
|
229
|
+
{"kind", "source_role_id"},
|
|
230
|
+
name=f"{name}.comparison_anchor",
|
|
231
|
+
)
|
|
232
|
+
kind = raw_anchor["kind"]
|
|
233
|
+
source_role_id = raw_anchor["source_role_id"]
|
|
234
|
+
if type(kind) is not str or (
|
|
235
|
+
source_role_id is not None and type(source_role_id) is not str
|
|
236
|
+
):
|
|
237
|
+
raise TypeError(f"{name}.comparison_anchor has non-string fields")
|
|
238
|
+
anchor = MetricComparisonAnchor(
|
|
239
|
+
kind=MetricComparisonAnchorKind(kind),
|
|
240
|
+
source_role_id=source_role_id,
|
|
241
|
+
)
|
|
242
|
+
prediction = MetricEffectPrediction(
|
|
243
|
+
metric_id=metric_id,
|
|
244
|
+
direction=MetricEffectDirection(direction),
|
|
245
|
+
comparison_anchor=anchor,
|
|
246
|
+
)
|
|
247
|
+
if prediction.to_record() != record:
|
|
248
|
+
raise ValueError(f"{name} is not the canonical prediction record")
|
|
249
|
+
return prediction
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _decode_insight_draft(value: object, *, name: str) -> InsightDraft:
|
|
253
|
+
record = _plain_object(value, name=name)
|
|
254
|
+
schema_version = record.get("schema_version")
|
|
255
|
+
base_fields = {
|
|
256
|
+
"schema_version",
|
|
257
|
+
"claim",
|
|
258
|
+
"trigger",
|
|
259
|
+
"mechanism",
|
|
260
|
+
"affected_paths",
|
|
261
|
+
"evidence_summary",
|
|
262
|
+
"confidence",
|
|
263
|
+
"evidence_contrast_ids",
|
|
264
|
+
"effect_predictions",
|
|
265
|
+
"recommended_option_families",
|
|
266
|
+
"recommended_option_ids",
|
|
267
|
+
"action_template",
|
|
268
|
+
"falsification_condition",
|
|
269
|
+
}
|
|
270
|
+
semantic_fields = {
|
|
271
|
+
"insight_kind",
|
|
272
|
+
"consumer_scopes",
|
|
273
|
+
"factor_capabilities",
|
|
274
|
+
}
|
|
275
|
+
if schema_version != 2:
|
|
276
|
+
raise ValueError(f"{name} must use semantic-v3 InsightDraft schema version 2")
|
|
277
|
+
_exact_keys(record, base_fields | semantic_fields, name=name)
|
|
278
|
+
text_fields = ("claim", "trigger", "mechanism", "evidence_summary")
|
|
279
|
+
if any(type(record[field]) is not str for field in text_fields):
|
|
280
|
+
raise TypeError(f"{name} prose fields must be exact strings")
|
|
281
|
+
confidence = record["confidence"]
|
|
282
|
+
if type(confidence) is not float:
|
|
283
|
+
raise TypeError(f"{name}.confidence must be an exact JSON float")
|
|
284
|
+
action_template = record["action_template"]
|
|
285
|
+
falsification_condition = record["falsification_condition"]
|
|
286
|
+
insight_kind = record["insight_kind"]
|
|
287
|
+
if (
|
|
288
|
+
type(action_template) is not str
|
|
289
|
+
or type(falsification_condition) is not str
|
|
290
|
+
or type(insight_kind) is not str
|
|
291
|
+
):
|
|
292
|
+
raise TypeError(f"{name} actionable semantic fields must be exact strings")
|
|
293
|
+
predictions = tuple(
|
|
294
|
+
_decode_metric_prediction(item, name=f"{name}.effect_predictions[{index}]")
|
|
295
|
+
for index, item in enumerate(
|
|
296
|
+
_object_tuple(
|
|
297
|
+
record["effect_predictions"],
|
|
298
|
+
name=f"{name}.effect_predictions",
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
consumer_scopes = tuple(
|
|
303
|
+
ReflectionConsumerScope(item)
|
|
304
|
+
for item in _string_tuple(
|
|
305
|
+
record["consumer_scopes"],
|
|
306
|
+
name=f"{name}.consumer_scopes",
|
|
307
|
+
)
|
|
308
|
+
)
|
|
309
|
+
draft = InsightDraft(
|
|
310
|
+
claim=record["claim"],
|
|
311
|
+
trigger=record["trigger"],
|
|
312
|
+
mechanism=record["mechanism"],
|
|
313
|
+
affected_paths=_string_tuple(
|
|
314
|
+
record["affected_paths"],
|
|
315
|
+
name=f"{name}.affected_paths",
|
|
316
|
+
),
|
|
317
|
+
evidence_summary=record["evidence_summary"],
|
|
318
|
+
confidence=confidence,
|
|
319
|
+
evidence_contrast_ids=_string_tuple(
|
|
320
|
+
record["evidence_contrast_ids"],
|
|
321
|
+
name=f"{name}.evidence_contrast_ids",
|
|
322
|
+
),
|
|
323
|
+
effect_predictions=predictions,
|
|
324
|
+
recommended_option_families=_string_tuple(
|
|
325
|
+
record["recommended_option_families"],
|
|
326
|
+
name=f"{name}.recommended_option_families",
|
|
327
|
+
),
|
|
328
|
+
recommended_option_ids=_string_tuple(
|
|
329
|
+
record["recommended_option_ids"],
|
|
330
|
+
name=f"{name}.recommended_option_ids",
|
|
331
|
+
),
|
|
332
|
+
action_template=action_template,
|
|
333
|
+
falsification_condition=falsification_condition,
|
|
334
|
+
insight_kind=ReflectionInsightKind(insight_kind),
|
|
335
|
+
consumer_scopes=consumer_scopes,
|
|
336
|
+
factor_capabilities=_string_tuple(
|
|
337
|
+
record["factor_capabilities"],
|
|
338
|
+
name=f"{name}.factor_capabilities",
|
|
339
|
+
),
|
|
340
|
+
)
|
|
341
|
+
if draft.content_record() != record:
|
|
342
|
+
raise ValueError(f"{name} is not the canonical InsightDraft content record")
|
|
343
|
+
return draft
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _decode_insight_contract(value: object) -> ReflectionInsightContract:
|
|
347
|
+
record = _plain_object(value, name="insight_contract")
|
|
348
|
+
fields = {
|
|
349
|
+
"schema_version",
|
|
350
|
+
"contract_identity_sha256",
|
|
351
|
+
"required_metric_ids",
|
|
352
|
+
"allowed_option_families",
|
|
353
|
+
"allowed_option_ids",
|
|
354
|
+
"direction_vocabulary",
|
|
355
|
+
"allowed_decision_paths",
|
|
356
|
+
"allowed_insight_kinds",
|
|
357
|
+
"allowed_consumer_scopes",
|
|
358
|
+
"allowed_comparison_anchor_kinds",
|
|
359
|
+
"allowed_factor_capabilities",
|
|
360
|
+
"allowed_source_role_ids",
|
|
361
|
+
}
|
|
362
|
+
_exact_keys(record, fields, name="insight_contract")
|
|
363
|
+
if record["schema_version"] != 3:
|
|
364
|
+
raise ValueError("insight_contract must use semantic-v3 schema version 3")
|
|
365
|
+
contract = ReflectionInsightContract(
|
|
366
|
+
required_metric_ids=_string_tuple(
|
|
367
|
+
record["required_metric_ids"],
|
|
368
|
+
name="insight_contract.required_metric_ids",
|
|
369
|
+
),
|
|
370
|
+
allowed_option_families=_string_tuple(
|
|
371
|
+
record["allowed_option_families"],
|
|
372
|
+
name="insight_contract.allowed_option_families",
|
|
373
|
+
),
|
|
374
|
+
allowed_option_ids=_string_tuple(
|
|
375
|
+
record["allowed_option_ids"],
|
|
376
|
+
name="insight_contract.allowed_option_ids",
|
|
377
|
+
),
|
|
378
|
+
allowed_decision_paths=_string_tuple(
|
|
379
|
+
record["allowed_decision_paths"],
|
|
380
|
+
name="insight_contract.allowed_decision_paths",
|
|
381
|
+
),
|
|
382
|
+
allowed_insight_kinds=tuple(
|
|
383
|
+
ReflectionInsightKind(item)
|
|
384
|
+
for item in _string_tuple(
|
|
385
|
+
record["allowed_insight_kinds"],
|
|
386
|
+
name="insight_contract.allowed_insight_kinds",
|
|
387
|
+
)
|
|
388
|
+
),
|
|
389
|
+
allowed_consumer_scopes=tuple(
|
|
390
|
+
ReflectionConsumerScope(item)
|
|
391
|
+
for item in _string_tuple(
|
|
392
|
+
record["allowed_consumer_scopes"],
|
|
393
|
+
name="insight_contract.allowed_consumer_scopes",
|
|
394
|
+
)
|
|
395
|
+
),
|
|
396
|
+
allowed_comparison_anchor_kinds=tuple(
|
|
397
|
+
MetricComparisonAnchorKind(item)
|
|
398
|
+
for item in _string_tuple(
|
|
399
|
+
record["allowed_comparison_anchor_kinds"],
|
|
400
|
+
name="insight_contract.allowed_comparison_anchor_kinds",
|
|
401
|
+
)
|
|
402
|
+
),
|
|
403
|
+
allowed_factor_capabilities=_string_tuple(
|
|
404
|
+
record["allowed_factor_capabilities"],
|
|
405
|
+
name="insight_contract.allowed_factor_capabilities",
|
|
406
|
+
),
|
|
407
|
+
allowed_source_role_ids=_string_tuple(
|
|
408
|
+
record["allowed_source_role_ids"],
|
|
409
|
+
name="insight_contract.allowed_source_role_ids",
|
|
410
|
+
),
|
|
411
|
+
)
|
|
412
|
+
if not contract.is_semantic_v3 or contract.to_record() != record:
|
|
413
|
+
raise ValueError("insight_contract is not its canonical semantic-v3 record")
|
|
414
|
+
return contract
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _decode_evidence_catalog(value: object) -> ReflectionEvidenceCatalog:
|
|
418
|
+
record = _plain_object(value, name="evidence_catalog")
|
|
419
|
+
_exact_keys(
|
|
420
|
+
record,
|
|
421
|
+
{"schema_version", "entries", "catalog_identity_sha256"},
|
|
422
|
+
name="evidence_catalog",
|
|
423
|
+
)
|
|
424
|
+
if record["schema_version"] != 1:
|
|
425
|
+
raise ValueError("evidence_catalog must use schema version 1")
|
|
426
|
+
entries_list: list[ReflectionEvidenceCatalogEntry] = []
|
|
427
|
+
for item in _object_tuple(record["entries"], name="evidence_catalog.entries"):
|
|
428
|
+
_exact_keys(
|
|
429
|
+
item,
|
|
430
|
+
{"citation_key", "contrast_id"},
|
|
431
|
+
name="evidence_catalog entry",
|
|
432
|
+
)
|
|
433
|
+
citation_key = item["citation_key"]
|
|
434
|
+
contrast_id = item["contrast_id"]
|
|
435
|
+
if type(citation_key) is not str or type(contrast_id) is not str:
|
|
436
|
+
raise TypeError("catalog entry fields must be exact strings")
|
|
437
|
+
entries_list.append(ReflectionEvidenceCatalogEntry(citation_key, contrast_id))
|
|
438
|
+
entries = tuple(entries_list)
|
|
439
|
+
catalog = ReflectionEvidenceCatalog(entries)
|
|
440
|
+
if catalog.to_record() != record:
|
|
441
|
+
raise ValueError("evidence_catalog is not its canonical record")
|
|
442
|
+
return catalog
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _decode_finite_action_binding(
|
|
446
|
+
value: object,
|
|
447
|
+
*,
|
|
448
|
+
name: str,
|
|
449
|
+
) -> FiniteActionEvidenceBinding:
|
|
450
|
+
record = _plain_object(value, name=name)
|
|
451
|
+
_exact_keys(
|
|
452
|
+
record,
|
|
453
|
+
{
|
|
454
|
+
"schema_version",
|
|
455
|
+
"contrast_id",
|
|
456
|
+
"option_id",
|
|
457
|
+
"family",
|
|
458
|
+
"option_identity_sha256",
|
|
459
|
+
"contract_identity_sha256",
|
|
460
|
+
"binding_identity_sha256",
|
|
461
|
+
},
|
|
462
|
+
name=name,
|
|
463
|
+
)
|
|
464
|
+
for field_name in (
|
|
465
|
+
"contrast_id",
|
|
466
|
+
"option_id",
|
|
467
|
+
"family",
|
|
468
|
+
"option_identity_sha256",
|
|
469
|
+
"contract_identity_sha256",
|
|
470
|
+
):
|
|
471
|
+
if type(record[field_name]) is not str:
|
|
472
|
+
raise TypeError(f"{name}.{field_name} must be an exact string")
|
|
473
|
+
binding = FiniteActionEvidenceBinding(
|
|
474
|
+
contrast_id=record["contrast_id"],
|
|
475
|
+
option_id=record["option_id"],
|
|
476
|
+
family=record["family"],
|
|
477
|
+
option_identity_sha256=record["option_identity_sha256"],
|
|
478
|
+
contract_identity_sha256=record["contract_identity_sha256"],
|
|
479
|
+
)
|
|
480
|
+
if binding.to_record() != record:
|
|
481
|
+
raise ValueError(f"{name} is not its canonical record")
|
|
482
|
+
return binding
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def _decode_empirical_snapshot(
|
|
486
|
+
value: object,
|
|
487
|
+
*,
|
|
488
|
+
name: str,
|
|
489
|
+
) -> EmpiricalEvidenceSnapshot:
|
|
490
|
+
record = _plain_object(value, name=name)
|
|
491
|
+
_exact_keys(
|
|
492
|
+
record,
|
|
493
|
+
{
|
|
494
|
+
"schema_version",
|
|
495
|
+
"contrast_id",
|
|
496
|
+
"fact_schema_id",
|
|
497
|
+
"fact_schema_version",
|
|
498
|
+
"fact_schema_definition_sha256",
|
|
499
|
+
"facts",
|
|
500
|
+
"optimization_semantics_definition_sha256",
|
|
501
|
+
"action_semantics_definition_sha256",
|
|
502
|
+
"snapshot_sha256",
|
|
503
|
+
},
|
|
504
|
+
name=name,
|
|
505
|
+
)
|
|
506
|
+
string_fields = (
|
|
507
|
+
"contrast_id",
|
|
508
|
+
"fact_schema_id",
|
|
509
|
+
"fact_schema_definition_sha256",
|
|
510
|
+
)
|
|
511
|
+
if any(type(record[field_name]) is not str for field_name in string_fields):
|
|
512
|
+
raise TypeError(f"{name} identity fields must be exact strings")
|
|
513
|
+
if type(record["fact_schema_version"]) is not int:
|
|
514
|
+
raise TypeError(f"{name}.fact_schema_version must be an exact integer")
|
|
515
|
+
for field_name in (
|
|
516
|
+
"optimization_semantics_definition_sha256",
|
|
517
|
+
"action_semantics_definition_sha256",
|
|
518
|
+
):
|
|
519
|
+
field_value = record[field_name]
|
|
520
|
+
if field_value is not None and type(field_value) is not str:
|
|
521
|
+
raise TypeError(f"{name}.{field_name} must be a string or null")
|
|
522
|
+
facts = freeze_json(_plain_object(record["facts"], name=f"{name}.facts"))
|
|
523
|
+
if type(facts) is not FrozenJsonObject:
|
|
524
|
+
raise AssertionError("empirical facts did not freeze to an object")
|
|
525
|
+
snapshot = EmpiricalEvidenceSnapshot(
|
|
526
|
+
contrast_id=record["contrast_id"],
|
|
527
|
+
fact_schema_id=record["fact_schema_id"],
|
|
528
|
+
fact_schema_version=record["fact_schema_version"],
|
|
529
|
+
fact_schema_definition_sha256=record["fact_schema_definition_sha256"],
|
|
530
|
+
facts=facts,
|
|
531
|
+
optimization_semantics_definition_sha256=record[
|
|
532
|
+
"optimization_semantics_definition_sha256"
|
|
533
|
+
],
|
|
534
|
+
action_semantics_definition_sha256=record["action_semantics_definition_sha256"],
|
|
535
|
+
)
|
|
536
|
+
if snapshot.to_record() != record:
|
|
537
|
+
raise ValueError(f"{name} is not its canonical record")
|
|
538
|
+
return snapshot
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
@dataclass(frozen=True, slots=True)
|
|
542
|
+
class CampaignReflectionLearningRecord:
|
|
543
|
+
"""Canonical provider-independent input to reflection learning.
|
|
544
|
+
|
|
545
|
+
The opaque campaign result may carry telemetry or runner-specific trace data
|
|
546
|
+
beside this record. Only this exact nested schema may create memory cards.
|
|
547
|
+
Engine-owned source IDs and empirical snapshots are therefore separated from
|
|
548
|
+
model-authored :class:`InsightDraft` content before projection.
|
|
549
|
+
"""
|
|
550
|
+
|
|
551
|
+
reflection_generation_request_sha256: str
|
|
552
|
+
reflection_call_id: LLMCallId
|
|
553
|
+
source_generation: int
|
|
554
|
+
source_stage_receipt_sha256: str
|
|
555
|
+
origin_cutoff_event_index: int
|
|
556
|
+
source_operator_invocation_ids: tuple[OperatorInvocationId, ...]
|
|
557
|
+
source_candidate_ids: tuple[CandidateId, ...]
|
|
558
|
+
evidence_catalog: ReflectionEvidenceCatalog
|
|
559
|
+
insight_contract: ReflectionInsightContract
|
|
560
|
+
insights: tuple[InsightDraft, ...]
|
|
561
|
+
finite_action_bindings: tuple[FiniteActionEvidenceBinding, ...]
|
|
562
|
+
empirical_evidence: tuple[EmpiricalEvidenceSnapshot, ...]
|
|
563
|
+
record_sha256: str = field(init=False)
|
|
564
|
+
|
|
565
|
+
def __post_init__(self) -> None:
|
|
566
|
+
require_sha256(
|
|
567
|
+
self.reflection_generation_request_sha256,
|
|
568
|
+
"reflection_generation_request_sha256",
|
|
569
|
+
)
|
|
570
|
+
if type(self.reflection_call_id) is not LLMCallId:
|
|
571
|
+
raise TypeError("reflection_call_id must be an exact LLMCallId")
|
|
572
|
+
LLMCallId.__post_init__(self.reflection_call_id)
|
|
573
|
+
if type(self.source_generation) is not int or self.source_generation <= 0:
|
|
574
|
+
raise ValueError("source_generation must be a positive exact integer")
|
|
575
|
+
require_sha256(
|
|
576
|
+
self.source_stage_receipt_sha256,
|
|
577
|
+
"source_stage_receipt_sha256",
|
|
578
|
+
)
|
|
579
|
+
if (
|
|
580
|
+
type(self.origin_cutoff_event_index) is not int
|
|
581
|
+
or self.origin_cutoff_event_index < 0
|
|
582
|
+
):
|
|
583
|
+
raise ValueError("origin_cutoff_event_index must be non-negative")
|
|
584
|
+
for values, expected_type, name in (
|
|
585
|
+
(
|
|
586
|
+
self.source_operator_invocation_ids,
|
|
587
|
+
OperatorInvocationId,
|
|
588
|
+
"source_operator_invocation_ids",
|
|
589
|
+
),
|
|
590
|
+
(self.source_candidate_ids, CandidateId, "source_candidate_ids"),
|
|
591
|
+
):
|
|
592
|
+
if (
|
|
593
|
+
type(values) is not tuple
|
|
594
|
+
or not values
|
|
595
|
+
or any(type(item) is not expected_type for item in values)
|
|
596
|
+
):
|
|
597
|
+
raise TypeError(f"{name} must contain exact non-empty IDs")
|
|
598
|
+
for item in values:
|
|
599
|
+
expected_type.__post_init__(item)
|
|
600
|
+
if values != tuple(sorted(set(values))):
|
|
601
|
+
raise ValueError(f"{name} must be unique and canonical")
|
|
602
|
+
if type(self.evidence_catalog) is not ReflectionEvidenceCatalog:
|
|
603
|
+
raise TypeError("evidence_catalog must be exact")
|
|
604
|
+
ReflectionEvidenceCatalog.__post_init__(self.evidence_catalog)
|
|
605
|
+
if type(self.insight_contract) is not ReflectionInsightContract:
|
|
606
|
+
raise TypeError("insight_contract must be exact")
|
|
607
|
+
ReflectionInsightContract.__post_init__(self.insight_contract)
|
|
608
|
+
if not self.insight_contract.is_semantic_v3:
|
|
609
|
+
raise ValueError("campaign reflection learning requires semantic-v3")
|
|
610
|
+
if (
|
|
611
|
+
type(self.insights) is not tuple
|
|
612
|
+
or not self.insights
|
|
613
|
+
or any(type(item) is not InsightDraft for item in self.insights)
|
|
614
|
+
):
|
|
615
|
+
raise ValueError("insights must contain exact InsightDraft values")
|
|
616
|
+
for draft in self.insights:
|
|
617
|
+
validate_reflection_insight_draft(draft, self.insight_contract)
|
|
618
|
+
if len({draft.content_sha256 for draft in self.insights}) != len(self.insights):
|
|
619
|
+
raise ValueError("reflection learning record repeats an insight draft")
|
|
620
|
+
if type(self.finite_action_bindings) is not tuple or any(
|
|
621
|
+
type(item) is not FiniteActionEvidenceBinding
|
|
622
|
+
for item in self.finite_action_bindings
|
|
623
|
+
):
|
|
624
|
+
raise TypeError("finite_action_bindings must contain exact bindings")
|
|
625
|
+
for item in self.finite_action_bindings:
|
|
626
|
+
FiniteActionEvidenceBinding.__post_init__(item)
|
|
627
|
+
binding_ids = tuple(item.contrast_id for item in self.finite_action_bindings)
|
|
628
|
+
if binding_ids != tuple(sorted(set(binding_ids))):
|
|
629
|
+
raise ValueError("finite action bindings must use canonical contrast order")
|
|
630
|
+
if type(self.empirical_evidence) is not tuple or any(
|
|
631
|
+
type(item) is not EmpiricalEvidenceSnapshot
|
|
632
|
+
for item in self.empirical_evidence
|
|
633
|
+
):
|
|
634
|
+
raise TypeError("empirical_evidence must contain exact snapshots")
|
|
635
|
+
for item in self.empirical_evidence:
|
|
636
|
+
EmpiricalEvidenceSnapshot.__post_init__(item)
|
|
637
|
+
empirical_ids = tuple(item.contrast_id for item in self.empirical_evidence)
|
|
638
|
+
if empirical_ids != self.evidence_catalog.contrast_ids:
|
|
639
|
+
raise ValueError(
|
|
640
|
+
"empirical evidence must exactly cover the request evidence catalog"
|
|
641
|
+
)
|
|
642
|
+
if not set(binding_ids).issubset(self.evidence_catalog.contrast_ids):
|
|
643
|
+
raise ValueError("finite action binding names a foreign contrast")
|
|
644
|
+
for draft in self.insights:
|
|
645
|
+
ReflectedInsightBatchItem(draft, self._lineage_for(draft))
|
|
646
|
+
object.__setattr__(
|
|
647
|
+
self,
|
|
648
|
+
"record_sha256",
|
|
649
|
+
_hash(_REFLECTION_LEARNING_RECORD_DOMAIN, self._unsigned_record()),
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
def _lineage_for(self, draft: InsightDraft) -> InsightEvidenceLineage:
|
|
653
|
+
cited = draft.evidence_contrast_ids
|
|
654
|
+
return InsightEvidenceLineage(
|
|
655
|
+
reflection_call_id=self.reflection_call_id,
|
|
656
|
+
source_operator_invocation_ids=self.source_operator_invocation_ids,
|
|
657
|
+
source_candidate_ids=self.source_candidate_ids,
|
|
658
|
+
available_contrast_ids=self.evidence_catalog.contrast_ids,
|
|
659
|
+
cited_contrast_ids=cited,
|
|
660
|
+
finite_action_bindings=tuple(
|
|
661
|
+
item
|
|
662
|
+
for item in self.finite_action_bindings
|
|
663
|
+
if item.contrast_id in cited
|
|
664
|
+
),
|
|
665
|
+
empirical_evidence=tuple(
|
|
666
|
+
item for item in self.empirical_evidence if item.contrast_id in cited
|
|
667
|
+
),
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
def lineage_for(self, draft: InsightDraft) -> InsightEvidenceLineage:
|
|
671
|
+
self.__post_init__()
|
|
672
|
+
if type(draft) is not InsightDraft or draft not in self.insights:
|
|
673
|
+
raise ValueError("draft is not part of this reflection learning record")
|
|
674
|
+
return self._lineage_for(draft)
|
|
675
|
+
|
|
676
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
677
|
+
return {
|
|
678
|
+
"schema_version": 1,
|
|
679
|
+
"reflection_generation_request_sha256": (
|
|
680
|
+
self.reflection_generation_request_sha256
|
|
681
|
+
),
|
|
682
|
+
"reflection_call_id": self.reflection_call_id.value,
|
|
683
|
+
"source_generation": self.source_generation,
|
|
684
|
+
"source_stage_receipt_sha256": self.source_stage_receipt_sha256,
|
|
685
|
+
"origin_cutoff_event_index": self.origin_cutoff_event_index,
|
|
686
|
+
"source_operator_invocation_ids": [
|
|
687
|
+
item.value for item in self.source_operator_invocation_ids
|
|
688
|
+
],
|
|
689
|
+
"source_candidate_ids": [item.value for item in self.source_candidate_ids],
|
|
690
|
+
"evidence_catalog": self.evidence_catalog.to_record(),
|
|
691
|
+
"insight_contract": self.insight_contract.to_record(),
|
|
692
|
+
"insights": [item.content_record() for item in self.insights],
|
|
693
|
+
"finite_action_bindings": [
|
|
694
|
+
item.to_record() for item in self.finite_action_bindings
|
|
695
|
+
],
|
|
696
|
+
"empirical_evidence": [
|
|
697
|
+
item.to_record() for item in self.empirical_evidence
|
|
698
|
+
],
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
def to_record(self) -> dict[str, object]:
|
|
702
|
+
self.__post_init__()
|
|
703
|
+
return {**self._unsigned_record(), "record_sha256": self.record_sha256}
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
@dataclass(frozen=True, slots=True)
|
|
707
|
+
class CampaignReflectionLearningRecordCodec:
|
|
708
|
+
"""Strict codec for the one canonical nested campaign learning record."""
|
|
709
|
+
|
|
710
|
+
@staticmethod
|
|
711
|
+
def encode(record: CampaignReflectionLearningRecord) -> FrozenJsonObject:
|
|
712
|
+
if type(record) is not CampaignReflectionLearningRecord:
|
|
713
|
+
raise TypeError("record must be exact CampaignReflectionLearningRecord")
|
|
714
|
+
CampaignReflectionLearningRecord.__post_init__(record)
|
|
715
|
+
return _object({CAMPAIGN_REFLECTION_LEARNING_RECORD_KEY: record.to_record()})
|
|
716
|
+
|
|
717
|
+
@staticmethod
|
|
718
|
+
def decode(result: FrozenJsonObject) -> CampaignReflectionLearningRecord:
|
|
719
|
+
if type(result) is not FrozenJsonObject or freeze_json(result) is not result:
|
|
720
|
+
raise TypeError("result must be an exact frozen object")
|
|
721
|
+
outer = thaw_json(result)
|
|
722
|
+
if type(outer) is not dict: # pragma: no cover - exact root above.
|
|
723
|
+
raise AssertionError("frozen reflection result thawed to a non-object")
|
|
724
|
+
if CAMPAIGN_REFLECTION_LEARNING_RECORD_KEY not in outer:
|
|
725
|
+
raise ValueError("reflection result omits the canonical learning record")
|
|
726
|
+
raw = _plain_object(
|
|
727
|
+
outer[CAMPAIGN_REFLECTION_LEARNING_RECORD_KEY],
|
|
728
|
+
name=CAMPAIGN_REFLECTION_LEARNING_RECORD_KEY,
|
|
729
|
+
)
|
|
730
|
+
fields = {
|
|
731
|
+
"schema_version",
|
|
732
|
+
"reflection_generation_request_sha256",
|
|
733
|
+
"reflection_call_id",
|
|
734
|
+
"source_generation",
|
|
735
|
+
"source_stage_receipt_sha256",
|
|
736
|
+
"origin_cutoff_event_index",
|
|
737
|
+
"source_operator_invocation_ids",
|
|
738
|
+
"source_candidate_ids",
|
|
739
|
+
"evidence_catalog",
|
|
740
|
+
"insight_contract",
|
|
741
|
+
"insights",
|
|
742
|
+
"finite_action_bindings",
|
|
743
|
+
"empirical_evidence",
|
|
744
|
+
"record_sha256",
|
|
745
|
+
}
|
|
746
|
+
_exact_keys(raw, fields, name=CAMPAIGN_REFLECTION_LEARNING_RECORD_KEY)
|
|
747
|
+
if raw["schema_version"] != 1:
|
|
748
|
+
raise ValueError("unsupported campaign reflection learning schema")
|
|
749
|
+
for field_name in (
|
|
750
|
+
"reflection_generation_request_sha256",
|
|
751
|
+
"reflection_call_id",
|
|
752
|
+
"source_stage_receipt_sha256",
|
|
753
|
+
):
|
|
754
|
+
if type(raw[field_name]) is not str:
|
|
755
|
+
raise TypeError(f"{field_name} must be an exact string")
|
|
756
|
+
if (
|
|
757
|
+
type(raw["source_generation"]) is not int
|
|
758
|
+
or type(raw["origin_cutoff_event_index"]) is not int
|
|
759
|
+
):
|
|
760
|
+
raise TypeError("reflection generation and cutoff must be exact integers")
|
|
761
|
+
record = CampaignReflectionLearningRecord(
|
|
762
|
+
reflection_generation_request_sha256=raw[
|
|
763
|
+
"reflection_generation_request_sha256"
|
|
764
|
+
],
|
|
765
|
+
reflection_call_id=LLMCallId(raw["reflection_call_id"]),
|
|
766
|
+
source_generation=raw["source_generation"],
|
|
767
|
+
source_stage_receipt_sha256=raw["source_stage_receipt_sha256"],
|
|
768
|
+
origin_cutoff_event_index=raw["origin_cutoff_event_index"],
|
|
769
|
+
source_operator_invocation_ids=tuple(
|
|
770
|
+
OperatorInvocationId(item)
|
|
771
|
+
for item in _string_tuple(
|
|
772
|
+
raw["source_operator_invocation_ids"],
|
|
773
|
+
name="source_operator_invocation_ids",
|
|
774
|
+
)
|
|
775
|
+
),
|
|
776
|
+
source_candidate_ids=tuple(
|
|
777
|
+
CandidateId(item)
|
|
778
|
+
for item in _string_tuple(
|
|
779
|
+
raw["source_candidate_ids"],
|
|
780
|
+
name="source_candidate_ids",
|
|
781
|
+
)
|
|
782
|
+
),
|
|
783
|
+
evidence_catalog=_decode_evidence_catalog(raw["evidence_catalog"]),
|
|
784
|
+
insight_contract=_decode_insight_contract(raw["insight_contract"]),
|
|
785
|
+
insights=tuple(
|
|
786
|
+
_decode_insight_draft(item, name=f"insights[{index}]")
|
|
787
|
+
for index, item in enumerate(
|
|
788
|
+
_object_tuple(raw["insights"], name="insights")
|
|
789
|
+
)
|
|
790
|
+
),
|
|
791
|
+
finite_action_bindings=tuple(
|
|
792
|
+
_decode_finite_action_binding(
|
|
793
|
+
item,
|
|
794
|
+
name=f"finite_action_bindings[{index}]",
|
|
795
|
+
)
|
|
796
|
+
for index, item in enumerate(
|
|
797
|
+
_object_tuple(
|
|
798
|
+
raw["finite_action_bindings"],
|
|
799
|
+
name="finite_action_bindings",
|
|
800
|
+
)
|
|
801
|
+
)
|
|
802
|
+
),
|
|
803
|
+
empirical_evidence=tuple(
|
|
804
|
+
_decode_empirical_snapshot(
|
|
805
|
+
item,
|
|
806
|
+
name=f"empirical_evidence[{index}]",
|
|
807
|
+
)
|
|
808
|
+
for index, item in enumerate(
|
|
809
|
+
_object_tuple(raw["empirical_evidence"], name="empirical_evidence")
|
|
810
|
+
)
|
|
811
|
+
),
|
|
812
|
+
)
|
|
813
|
+
if record.to_record() != raw:
|
|
814
|
+
raise ValueError("campaign reflection learning record is not canonical")
|
|
815
|
+
return record
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
@dataclass(frozen=True, slots=True)
|
|
819
|
+
class CompiledCampaignInsightSemantics:
|
|
820
|
+
"""Workload compilation output bound to one exact draft and evidence lineage."""
|
|
821
|
+
|
|
822
|
+
draft_content_sha256: str
|
|
823
|
+
insight_contract_identity_sha256: str
|
|
824
|
+
evidence_lineage_sha256: str
|
|
825
|
+
trigger: TypedEvidencePredicate
|
|
826
|
+
old_value_predicate: TypedEvidencePredicate
|
|
827
|
+
new_action: TypedEvidencePredicate
|
|
828
|
+
matcher_definition_sha256: str
|
|
829
|
+
compiler_policy_id: str
|
|
830
|
+
compiler_policy_version: int
|
|
831
|
+
compiler_definition_sha256: str
|
|
832
|
+
compilation_sha256: str = field(init=False)
|
|
833
|
+
|
|
834
|
+
def __post_init__(self) -> None:
|
|
835
|
+
for name in (
|
|
836
|
+
"draft_content_sha256",
|
|
837
|
+
"insight_contract_identity_sha256",
|
|
838
|
+
"evidence_lineage_sha256",
|
|
839
|
+
"matcher_definition_sha256",
|
|
840
|
+
"compiler_definition_sha256",
|
|
841
|
+
):
|
|
842
|
+
require_sha256(getattr(self, name), name)
|
|
843
|
+
for name in ("trigger", "old_value_predicate", "new_action"):
|
|
844
|
+
value = getattr(self, name)
|
|
845
|
+
if type(value) is not TypedEvidencePredicate:
|
|
846
|
+
raise TypeError(f"{name} must be an exact TypedEvidencePredicate")
|
|
847
|
+
TypedEvidencePredicate.__post_init__(value)
|
|
848
|
+
if (
|
|
849
|
+
type(self.compiler_policy_id) is not str
|
|
850
|
+
or _TOKEN.fullmatch(self.compiler_policy_id) is None
|
|
851
|
+
):
|
|
852
|
+
raise ValueError("compiler_policy_id must be canonical")
|
|
853
|
+
if (
|
|
854
|
+
type(self.compiler_policy_version) is not int
|
|
855
|
+
or self.compiler_policy_version <= 0
|
|
856
|
+
):
|
|
857
|
+
raise ValueError("compiler_policy_version must be positive")
|
|
858
|
+
object.__setattr__(
|
|
859
|
+
self,
|
|
860
|
+
"compilation_sha256",
|
|
861
|
+
_hash(_COMPILED_INSIGHT_SEMANTICS_DOMAIN, self._unsigned_record()),
|
|
862
|
+
)
|
|
863
|
+
|
|
864
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
865
|
+
return {
|
|
866
|
+
"schema_version": 1,
|
|
867
|
+
"draft_content_sha256": self.draft_content_sha256,
|
|
868
|
+
"insight_contract_identity_sha256": (self.insight_contract_identity_sha256),
|
|
869
|
+
"evidence_lineage_sha256": self.evidence_lineage_sha256,
|
|
870
|
+
"trigger_predicate_sha256": self.trigger.predicate_sha256,
|
|
871
|
+
"old_value_predicate_sha256": self.old_value_predicate.predicate_sha256,
|
|
872
|
+
"new_action_predicate_sha256": self.new_action.predicate_sha256,
|
|
873
|
+
"matcher_definition_sha256": self.matcher_definition_sha256,
|
|
874
|
+
"compiler": {
|
|
875
|
+
"policy_id": self.compiler_policy_id,
|
|
876
|
+
"policy_version": self.compiler_policy_version,
|
|
877
|
+
"definition_sha256": self.compiler_definition_sha256,
|
|
878
|
+
},
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
def to_record(self) -> dict[str, object]:
|
|
882
|
+
self.__post_init__()
|
|
883
|
+
return {
|
|
884
|
+
**self._unsigned_record(),
|
|
885
|
+
"compilation_sha256": self.compilation_sha256,
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
@runtime_checkable
|
|
890
|
+
class CampaignInsightSemanticCompiler(Protocol):
|
|
891
|
+
"""Irreducible workload seam for replayable trigger/action semantics."""
|
|
892
|
+
|
|
893
|
+
policy_id: str
|
|
894
|
+
policy_version: int
|
|
895
|
+
definition_sha256: str
|
|
896
|
+
|
|
897
|
+
def compile(
|
|
898
|
+
self,
|
|
899
|
+
*,
|
|
900
|
+
draft: InsightDraft,
|
|
901
|
+
insight_contract: ReflectionInsightContract,
|
|
902
|
+
evidence_lineage: InsightEvidenceLineage,
|
|
903
|
+
) -> CompiledCampaignInsightSemantics: ...
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
@dataclass(frozen=True, slots=True)
|
|
907
|
+
class CampaignSemanticAuditPlanTemplate:
|
|
908
|
+
"""Reference-free semantic plan projected before memory allocates an ID."""
|
|
909
|
+
|
|
910
|
+
trigger: TypedEvidencePredicate
|
|
911
|
+
intervention: TypedInterventionSignature
|
|
912
|
+
predictions: tuple[HypothesisMetricPrediction, ...]
|
|
913
|
+
claim_strength: HypothesisClaimStrength
|
|
914
|
+
scope: HypothesisAuditScope
|
|
915
|
+
matcher_definition_sha256: str
|
|
916
|
+
origin_cutoff_event_index: int
|
|
917
|
+
minimum_support_clusters: int
|
|
918
|
+
minimum_support_instances: int
|
|
919
|
+
audit_policy_definition_sha256: str = GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256
|
|
920
|
+
template_sha256: str = field(init=False)
|
|
921
|
+
|
|
922
|
+
def __post_init__(self) -> None:
|
|
923
|
+
if type(self.trigger) is not TypedEvidencePredicate:
|
|
924
|
+
raise TypeError("trigger must be exact")
|
|
925
|
+
TypedEvidencePredicate.__post_init__(self.trigger)
|
|
926
|
+
if type(self.intervention) is not TypedInterventionSignature:
|
|
927
|
+
raise TypeError("intervention must be exact")
|
|
928
|
+
TypedInterventionSignature.__post_init__(self.intervention)
|
|
929
|
+
if (
|
|
930
|
+
type(self.predictions) is not tuple
|
|
931
|
+
or not self.predictions
|
|
932
|
+
or any(
|
|
933
|
+
type(value) is not HypothesisMetricPrediction
|
|
934
|
+
for value in self.predictions
|
|
935
|
+
)
|
|
936
|
+
):
|
|
937
|
+
raise ValueError("predictions must contain exact typed predictions")
|
|
938
|
+
for value in self.predictions:
|
|
939
|
+
HypothesisMetricPrediction.__post_init__(value)
|
|
940
|
+
if type(self.claim_strength) is not HypothesisClaimStrength:
|
|
941
|
+
raise TypeError("claim_strength must be exact")
|
|
942
|
+
HypothesisClaimStrength.__post_init__(self.claim_strength)
|
|
943
|
+
if type(self.scope) is not HypothesisAuditScope:
|
|
944
|
+
raise TypeError("scope must be exact")
|
|
945
|
+
HypothesisAuditScope.__post_init__(self.scope)
|
|
946
|
+
require_sha256(self.matcher_definition_sha256, "matcher_definition_sha256")
|
|
947
|
+
if (
|
|
948
|
+
type(self.origin_cutoff_event_index) is not int
|
|
949
|
+
or self.origin_cutoff_event_index < 0
|
|
950
|
+
):
|
|
951
|
+
raise ValueError("origin_cutoff_event_index must be non-negative")
|
|
952
|
+
for name in ("minimum_support_clusters", "minimum_support_instances"):
|
|
953
|
+
value = getattr(self, name)
|
|
954
|
+
if type(value) is not int or value <= 0:
|
|
955
|
+
raise ValueError(f"{name} must be a positive exact integer")
|
|
956
|
+
if (
|
|
957
|
+
self.audit_policy_definition_sha256
|
|
958
|
+
!= GLOBAL_FALSIFICATION_POLICY_DEFINITION_SHA256
|
|
959
|
+
):
|
|
960
|
+
raise ValueError("unsupported global falsification policy definition")
|
|
961
|
+
object.__setattr__(
|
|
962
|
+
self,
|
|
963
|
+
"template_sha256",
|
|
964
|
+
_hash(_TEMPLATE_DOMAIN, self._unsigned_record()),
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
968
|
+
return {
|
|
969
|
+
"schema_version": 1,
|
|
970
|
+
"trigger_predicate_sha256": self.trigger.predicate_sha256,
|
|
971
|
+
"intervention_signature_sha256": self.intervention.signature_sha256,
|
|
972
|
+
"predictions": [value.to_record() for value in self.predictions],
|
|
973
|
+
"claim_strength": self.claim_strength.to_record(),
|
|
974
|
+
"scope_sha256": self.scope.scope_sha256,
|
|
975
|
+
"matcher_definition_sha256": self.matcher_definition_sha256,
|
|
976
|
+
"origin_cutoff_event_index": self.origin_cutoff_event_index,
|
|
977
|
+
"minimum_support_clusters": self.minimum_support_clusters,
|
|
978
|
+
"minimum_support_instances": self.minimum_support_instances,
|
|
979
|
+
"audit_policy_definition_sha256": (self.audit_policy_definition_sha256),
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
def to_record(self) -> dict[str, object]:
|
|
983
|
+
self.__post_init__()
|
|
984
|
+
return {**self._unsigned_record(), "template_sha256": self.template_sha256}
|
|
985
|
+
|
|
986
|
+
def validate_source(
|
|
987
|
+
self,
|
|
988
|
+
draft: InsightDraft,
|
|
989
|
+
applicable_operator_kinds: tuple[str, ...],
|
|
990
|
+
) -> None:
|
|
991
|
+
"""Fail before memory publication if a template changes card semantics."""
|
|
992
|
+
|
|
993
|
+
if type(draft) is not InsightDraft:
|
|
994
|
+
raise TypeError("draft must be exact")
|
|
995
|
+
InsightDraft.__post_init__(draft)
|
|
996
|
+
_canonical_tokens(
|
|
997
|
+
applicable_operator_kinds,
|
|
998
|
+
name="applicable_operator_kinds",
|
|
999
|
+
pattern=_OPERATOR,
|
|
1000
|
+
)
|
|
1001
|
+
if not draft.has_semantic_contract or not draft.has_intervention_contract:
|
|
1002
|
+
raise ValueError("runtime reflection must be actionable semantic-v3")
|
|
1003
|
+
if draft.insight_kind not in {
|
|
1004
|
+
ReflectionInsightKind.EMPIRICAL_PREDICTIVE_RULE,
|
|
1005
|
+
ReflectionInsightKind.MECHANISTIC_CONJECTURE,
|
|
1006
|
+
}:
|
|
1007
|
+
raise ValueError("runtime reflection kind is not promotion-eligible")
|
|
1008
|
+
expected_strength = HypothesisClaimStrength(
|
|
1009
|
+
sufficiency=True,
|
|
1010
|
+
necessity=False,
|
|
1011
|
+
invariance=False,
|
|
1012
|
+
mechanistic_or_causal=(
|
|
1013
|
+
draft.insight_kind is ReflectionInsightKind.MECHANISTIC_CONJECTURE
|
|
1014
|
+
),
|
|
1015
|
+
)
|
|
1016
|
+
expected_predictions = tuple(
|
|
1017
|
+
(value.metric_id, value.direction, None, None)
|
|
1018
|
+
for value in draft.effect_predictions
|
|
1019
|
+
)
|
|
1020
|
+
observed_predictions = tuple(
|
|
1021
|
+
(
|
|
1022
|
+
value.metric_id,
|
|
1023
|
+
value.direction,
|
|
1024
|
+
value.minimum_delta,
|
|
1025
|
+
value.maximum_delta,
|
|
1026
|
+
)
|
|
1027
|
+
for value in self.predictions
|
|
1028
|
+
)
|
|
1029
|
+
if any(
|
|
1030
|
+
value.direction is MetricEffectDirection.UNKNOWN
|
|
1031
|
+
for value in draft.effect_predictions
|
|
1032
|
+
):
|
|
1033
|
+
raise ValueError("runtime reflection predictions must be known")
|
|
1034
|
+
if (
|
|
1035
|
+
draft.affected_paths != tuple(sorted(set(draft.affected_paths)))
|
|
1036
|
+
or self.intervention.affected_paths != draft.affected_paths
|
|
1037
|
+
or self.intervention.admissible_operator_families
|
|
1038
|
+
!= applicable_operator_kinds
|
|
1039
|
+
or observed_predictions != expected_predictions
|
|
1040
|
+
or self.claim_strength != expected_strength
|
|
1041
|
+
):
|
|
1042
|
+
raise ValueError("semantic audit template differs from reflection draft")
|
|
1043
|
+
|
|
1044
|
+
def bind(self, entry: InsightMemoryEntry) -> CampaignSemanticAuditPlan:
|
|
1045
|
+
if type(entry) is not InsightMemoryEntry:
|
|
1046
|
+
raise TypeError("entry must be exact")
|
|
1047
|
+
InsightMemoryEntry.__post_init__(entry)
|
|
1048
|
+
self.validate_source(entry.draft, entry.applicable_operator_kinds)
|
|
1049
|
+
return CampaignSemanticAuditPlan(
|
|
1050
|
+
reference=entry.reference,
|
|
1051
|
+
draft_content_sha256=entry.draft.content_sha256,
|
|
1052
|
+
draft_hypothesis_sha256=entry.draft.hypothesis_sha256,
|
|
1053
|
+
trigger=self.trigger,
|
|
1054
|
+
intervention=self.intervention,
|
|
1055
|
+
predictions=self.predictions,
|
|
1056
|
+
claim_strength=self.claim_strength,
|
|
1057
|
+
scope=self.scope,
|
|
1058
|
+
matcher_definition_sha256=self.matcher_definition_sha256,
|
|
1059
|
+
origin_cutoff_event_index=self.origin_cutoff_event_index,
|
|
1060
|
+
minimum_support_clusters=self.minimum_support_clusters,
|
|
1061
|
+
minimum_support_instances=self.minimum_support_instances,
|
|
1062
|
+
audit_policy_definition_sha256=(self.audit_policy_definition_sha256),
|
|
1063
|
+
)
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
@dataclass(frozen=True, slots=True)
|
|
1067
|
+
class CampaignReflectedInsightProjection:
|
|
1068
|
+
draft: InsightDraft
|
|
1069
|
+
evidence_lineage: InsightEvidenceLineage
|
|
1070
|
+
semantic_audit_template: CampaignSemanticAuditPlanTemplate
|
|
1071
|
+
|
|
1072
|
+
def __post_init__(self) -> None:
|
|
1073
|
+
if type(self.draft) is not InsightDraft:
|
|
1074
|
+
raise TypeError("draft must be exact")
|
|
1075
|
+
InsightDraft.__post_init__(self.draft)
|
|
1076
|
+
if type(self.evidence_lineage) is not InsightEvidenceLineage:
|
|
1077
|
+
raise TypeError("evidence_lineage must be exact")
|
|
1078
|
+
InsightEvidenceLineage.__post_init__(self.evidence_lineage)
|
|
1079
|
+
if type(self.semantic_audit_template) is not (
|
|
1080
|
+
CampaignSemanticAuditPlanTemplate
|
|
1081
|
+
):
|
|
1082
|
+
raise TypeError("semantic_audit_template must be exact")
|
|
1083
|
+
CampaignSemanticAuditPlanTemplate.__post_init__(self.semantic_audit_template)
|
|
1084
|
+
ReflectedInsightBatchItem(self.draft, self.evidence_lineage)
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
@dataclass(frozen=True, slots=True)
|
|
1088
|
+
class CampaignReflectionLearningProjection:
|
|
1089
|
+
reflection_request_sha256: str
|
|
1090
|
+
reflection_receipt_sha256: str
|
|
1091
|
+
reflection_result_sha256: str
|
|
1092
|
+
insights: tuple[CampaignReflectedInsightProjection, ...]
|
|
1093
|
+
applicable_operator_kinds: tuple[str, ...]
|
|
1094
|
+
diagnostic_operator_kind: str
|
|
1095
|
+
diagnostic_editable_paths: tuple[str, ...]
|
|
1096
|
+
initial_score: float
|
|
1097
|
+
projection_policy_id: str
|
|
1098
|
+
projection_policy_version: int
|
|
1099
|
+
projection_policy_definition_sha256: str
|
|
1100
|
+
projection_sha256: str = field(init=False)
|
|
1101
|
+
|
|
1102
|
+
def __post_init__(self) -> None:
|
|
1103
|
+
for name in (
|
|
1104
|
+
"reflection_request_sha256",
|
|
1105
|
+
"reflection_receipt_sha256",
|
|
1106
|
+
"reflection_result_sha256",
|
|
1107
|
+
"projection_policy_definition_sha256",
|
|
1108
|
+
):
|
|
1109
|
+
require_sha256(getattr(self, name), name)
|
|
1110
|
+
if (
|
|
1111
|
+
type(self.insights) is not tuple
|
|
1112
|
+
or not self.insights
|
|
1113
|
+
or any(
|
|
1114
|
+
type(value) is not CampaignReflectedInsightProjection
|
|
1115
|
+
for value in self.insights
|
|
1116
|
+
)
|
|
1117
|
+
):
|
|
1118
|
+
raise ValueError("insights must contain exact projections")
|
|
1119
|
+
for value in self.insights:
|
|
1120
|
+
CampaignReflectedInsightProjection.__post_init__(value)
|
|
1121
|
+
_canonical_tokens(
|
|
1122
|
+
self.applicable_operator_kinds,
|
|
1123
|
+
name="applicable_operator_kinds",
|
|
1124
|
+
pattern=_OPERATOR,
|
|
1125
|
+
)
|
|
1126
|
+
if (
|
|
1127
|
+
type(self.diagnostic_operator_kind) is not str
|
|
1128
|
+
or _OPERATOR.fullmatch(self.diagnostic_operator_kind) is None
|
|
1129
|
+
or self.diagnostic_operator_kind not in self.applicable_operator_kinds
|
|
1130
|
+
):
|
|
1131
|
+
raise ValueError("diagnostic_operator_kind must be explicitly applicable")
|
|
1132
|
+
_canonical_tokens(
|
|
1133
|
+
self.diagnostic_editable_paths,
|
|
1134
|
+
name="diagnostic_editable_paths",
|
|
1135
|
+
pattern=_PATH,
|
|
1136
|
+
)
|
|
1137
|
+
if any(
|
|
1138
|
+
not any(
|
|
1139
|
+
_paths_overlap(affected, editable)
|
|
1140
|
+
for editable in self.diagnostic_editable_paths
|
|
1141
|
+
)
|
|
1142
|
+
for item in self.insights
|
|
1143
|
+
for affected in item.draft.affected_paths
|
|
1144
|
+
):
|
|
1145
|
+
raise ValueError("diagnostic paths do not cover every reflection path")
|
|
1146
|
+
if type(self.initial_score) is not float or not math.isfinite(
|
|
1147
|
+
self.initial_score
|
|
1148
|
+
):
|
|
1149
|
+
raise TypeError("initial_score must be a finite canonical float")
|
|
1150
|
+
if (
|
|
1151
|
+
type(self.projection_policy_id) is not str
|
|
1152
|
+
or _TOKEN.fullmatch(self.projection_policy_id) is None
|
|
1153
|
+
):
|
|
1154
|
+
raise ValueError("projection_policy_id must be canonical")
|
|
1155
|
+
if (
|
|
1156
|
+
type(self.projection_policy_version) is not int
|
|
1157
|
+
or self.projection_policy_version <= 0
|
|
1158
|
+
):
|
|
1159
|
+
raise ValueError("projection_policy_version must be positive")
|
|
1160
|
+
for item in self.insights:
|
|
1161
|
+
item.semantic_audit_template.validate_source(
|
|
1162
|
+
item.draft,
|
|
1163
|
+
self.applicable_operator_kinds,
|
|
1164
|
+
)
|
|
1165
|
+
object.__setattr__(
|
|
1166
|
+
self,
|
|
1167
|
+
"projection_sha256",
|
|
1168
|
+
_hash(_REFLECTION_PROJECTION_DOMAIN, self._unsigned_record()),
|
|
1169
|
+
)
|
|
1170
|
+
|
|
1171
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1172
|
+
return {
|
|
1173
|
+
"schema_version": 1,
|
|
1174
|
+
"reflection_request_sha256": self.reflection_request_sha256,
|
|
1175
|
+
"reflection_receipt_sha256": self.reflection_receipt_sha256,
|
|
1176
|
+
"reflection_result_sha256": self.reflection_result_sha256,
|
|
1177
|
+
"insights": [
|
|
1178
|
+
{
|
|
1179
|
+
"draft_content_sha256": value.draft.content_sha256,
|
|
1180
|
+
"draft_hypothesis_sha256": value.draft.hypothesis_sha256,
|
|
1181
|
+
"evidence_lineage_sha256": value.evidence_lineage.identity_sha256,
|
|
1182
|
+
"semantic_audit_template_sha256": (
|
|
1183
|
+
value.semantic_audit_template.template_sha256
|
|
1184
|
+
),
|
|
1185
|
+
}
|
|
1186
|
+
for value in self.insights
|
|
1187
|
+
],
|
|
1188
|
+
"applicable_operator_kinds": list(self.applicable_operator_kinds),
|
|
1189
|
+
"diagnostic_operator_kind": self.diagnostic_operator_kind,
|
|
1190
|
+
"diagnostic_editable_paths": list(self.diagnostic_editable_paths),
|
|
1191
|
+
"initial_score_hex": self.initial_score.hex(),
|
|
1192
|
+
"projection_policy": {
|
|
1193
|
+
"policy_id": self.projection_policy_id,
|
|
1194
|
+
"policy_version": self.projection_policy_version,
|
|
1195
|
+
"definition_sha256": self.projection_policy_definition_sha256,
|
|
1196
|
+
},
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
def to_record(self) -> dict[str, object]:
|
|
1200
|
+
self.__post_init__()
|
|
1201
|
+
return {**self._unsigned_record(), "projection_sha256": self.projection_sha256}
|
|
1202
|
+
|
|
1203
|
+
|
|
1204
|
+
@runtime_checkable
|
|
1205
|
+
class CampaignReflectionLearningProjectionPort(Protocol):
|
|
1206
|
+
policy_id: str
|
|
1207
|
+
policy_version: int
|
|
1208
|
+
definition_sha256: str
|
|
1209
|
+
|
|
1210
|
+
def project(
|
|
1211
|
+
self,
|
|
1212
|
+
request: CampaignReflectionRequest,
|
|
1213
|
+
receipt: CampaignReflectionReceipt,
|
|
1214
|
+
result: FrozenJsonObject,
|
|
1215
|
+
) -> CampaignReflectionLearningProjection: ...
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
@dataclass(frozen=True, slots=True)
|
|
1219
|
+
class StructuredCampaignReflectionLearningProjector:
|
|
1220
|
+
"""Generic strict-record projector with injected workload semantics.
|
|
1221
|
+
|
|
1222
|
+
All structural joins, lineage construction, prediction projection, claim
|
|
1223
|
+
strength, and audit-template binding are application-owned. Only typed
|
|
1224
|
+
trigger/old-value/new-action compilation remains workload-owned.
|
|
1225
|
+
"""
|
|
1226
|
+
|
|
1227
|
+
semantic_compiler: CampaignInsightSemanticCompiler
|
|
1228
|
+
scope: HypothesisAuditScope
|
|
1229
|
+
applicable_operator_kinds: tuple[str, ...]
|
|
1230
|
+
diagnostic_operator_kind: str
|
|
1231
|
+
diagnostic_editable_paths: tuple[str, ...]
|
|
1232
|
+
initial_score: float = 0.0
|
|
1233
|
+
minimum_support_clusters: int = 2
|
|
1234
|
+
minimum_support_instances: int = 1
|
|
1235
|
+
policy_id: str = STRUCTURED_REFLECTION_PROJECTION_POLICY_ID
|
|
1236
|
+
policy_version: int = STRUCTURED_REFLECTION_PROJECTION_POLICY_VERSION
|
|
1237
|
+
definition_sha256: str = STRUCTURED_REFLECTION_PROJECTION_DEFINITION_SHA256
|
|
1238
|
+
|
|
1239
|
+
def __post_init__(self) -> None:
|
|
1240
|
+
if not isinstance(self.semantic_compiler, CampaignInsightSemanticCompiler):
|
|
1241
|
+
raise TypeError("semantic_compiler must implement its exact port")
|
|
1242
|
+
compiler = self.semantic_compiler
|
|
1243
|
+
if (
|
|
1244
|
+
type(compiler.policy_id) is not str
|
|
1245
|
+
or _TOKEN.fullmatch(compiler.policy_id) is None
|
|
1246
|
+
):
|
|
1247
|
+
raise ValueError("semantic compiler policy_id must be canonical")
|
|
1248
|
+
if type(compiler.policy_version) is not int or compiler.policy_version <= 0:
|
|
1249
|
+
raise ValueError("semantic compiler policy_version must be positive")
|
|
1250
|
+
require_sha256(
|
|
1251
|
+
compiler.definition_sha256,
|
|
1252
|
+
"semantic_compiler.definition_sha256",
|
|
1253
|
+
)
|
|
1254
|
+
if type(self.scope) is not HypothesisAuditScope:
|
|
1255
|
+
raise TypeError("scope must be an exact HypothesisAuditScope")
|
|
1256
|
+
HypothesisAuditScope.__post_init__(self.scope)
|
|
1257
|
+
_canonical_tokens(
|
|
1258
|
+
self.applicable_operator_kinds,
|
|
1259
|
+
name="applicable_operator_kinds",
|
|
1260
|
+
pattern=_OPERATOR,
|
|
1261
|
+
)
|
|
1262
|
+
if (
|
|
1263
|
+
type(self.diagnostic_operator_kind) is not str
|
|
1264
|
+
or _OPERATOR.fullmatch(self.diagnostic_operator_kind) is None
|
|
1265
|
+
or self.diagnostic_operator_kind not in self.applicable_operator_kinds
|
|
1266
|
+
):
|
|
1267
|
+
raise ValueError("diagnostic_operator_kind must be explicitly applicable")
|
|
1268
|
+
_canonical_tokens(
|
|
1269
|
+
self.diagnostic_editable_paths,
|
|
1270
|
+
name="diagnostic_editable_paths",
|
|
1271
|
+
pattern=_PATH,
|
|
1272
|
+
)
|
|
1273
|
+
if type(self.initial_score) is not float or not math.isfinite(
|
|
1274
|
+
self.initial_score
|
|
1275
|
+
):
|
|
1276
|
+
raise TypeError("initial_score must be a finite canonical float")
|
|
1277
|
+
for name in ("minimum_support_clusters", "minimum_support_instances"):
|
|
1278
|
+
value = getattr(self, name)
|
|
1279
|
+
if type(value) is not int or value <= 0:
|
|
1280
|
+
raise ValueError(f"{name} must be a positive exact integer")
|
|
1281
|
+
if type(self.policy_id) is not str or _TOKEN.fullmatch(self.policy_id) is None:
|
|
1282
|
+
raise ValueError("policy_id must be canonical")
|
|
1283
|
+
if type(self.policy_version) is not int or self.policy_version != 1:
|
|
1284
|
+
raise ValueError("unsupported structured projection policy version")
|
|
1285
|
+
if self.definition_sha256 != (
|
|
1286
|
+
STRUCTURED_REFLECTION_PROJECTION_DEFINITION_SHA256
|
|
1287
|
+
):
|
|
1288
|
+
raise ValueError("unsupported structured projection policy definition")
|
|
1289
|
+
|
|
1290
|
+
def _compile(
|
|
1291
|
+
self,
|
|
1292
|
+
*,
|
|
1293
|
+
draft: InsightDraft,
|
|
1294
|
+
contract: ReflectionInsightContract,
|
|
1295
|
+
lineage: InsightEvidenceLineage,
|
|
1296
|
+
) -> CompiledCampaignInsightSemantics:
|
|
1297
|
+
compiled = self.semantic_compiler.compile(
|
|
1298
|
+
draft=draft,
|
|
1299
|
+
insight_contract=contract,
|
|
1300
|
+
evidence_lineage=lineage,
|
|
1301
|
+
)
|
|
1302
|
+
if type(compiled) is not CompiledCampaignInsightSemantics:
|
|
1303
|
+
raise TypeError("semantic compiler returned a foreign value")
|
|
1304
|
+
CompiledCampaignInsightSemantics.__post_init__(compiled)
|
|
1305
|
+
observed = (
|
|
1306
|
+
compiled.draft_content_sha256,
|
|
1307
|
+
compiled.insight_contract_identity_sha256,
|
|
1308
|
+
compiled.evidence_lineage_sha256,
|
|
1309
|
+
compiled.compiler_policy_id,
|
|
1310
|
+
compiled.compiler_policy_version,
|
|
1311
|
+
compiled.compiler_definition_sha256,
|
|
1312
|
+
)
|
|
1313
|
+
expected = (
|
|
1314
|
+
draft.content_sha256,
|
|
1315
|
+
contract.identity_sha256,
|
|
1316
|
+
lineage.identity_sha256,
|
|
1317
|
+
self.semantic_compiler.policy_id,
|
|
1318
|
+
self.semantic_compiler.policy_version,
|
|
1319
|
+
self.semantic_compiler.definition_sha256,
|
|
1320
|
+
)
|
|
1321
|
+
if observed != expected:
|
|
1322
|
+
raise ValueError("semantic compilation belongs to foreign inputs or policy")
|
|
1323
|
+
return compiled
|
|
1324
|
+
|
|
1325
|
+
def project(
|
|
1326
|
+
self,
|
|
1327
|
+
request: CampaignReflectionRequest,
|
|
1328
|
+
receipt: CampaignReflectionReceipt,
|
|
1329
|
+
result: FrozenJsonObject,
|
|
1330
|
+
) -> CampaignReflectionLearningProjection:
|
|
1331
|
+
self.__post_init__()
|
|
1332
|
+
if type(request) is not CampaignReflectionRequest:
|
|
1333
|
+
raise TypeError("request must be an exact CampaignReflectionRequest")
|
|
1334
|
+
CampaignReflectionRequest.__post_init__(request)
|
|
1335
|
+
if type(receipt) is not CampaignReflectionReceipt:
|
|
1336
|
+
raise TypeError("receipt must be an exact CampaignReflectionReceipt")
|
|
1337
|
+
CampaignReflectionReceipt.__post_init__(receipt)
|
|
1338
|
+
if type(result) is not FrozenJsonObject or freeze_json(result) is not result:
|
|
1339
|
+
raise TypeError("result must be an exact frozen object")
|
|
1340
|
+
if (
|
|
1341
|
+
receipt.request_sha256 != request.request_sha256
|
|
1342
|
+
or receipt.quarantined_result != result
|
|
1343
|
+
):
|
|
1344
|
+
raise ValueError("reflection receipt differs from request/result")
|
|
1345
|
+
if receipt.status is not CampaignReflectionStatus.COMPLETED:
|
|
1346
|
+
raise ValueError("failed reflection cannot enter campaign learning")
|
|
1347
|
+
if receipt.logical_agent_calls != 1:
|
|
1348
|
+
raise ValueError(
|
|
1349
|
+
"reflection learning record v1 requires exactly one agentic call"
|
|
1350
|
+
)
|
|
1351
|
+
record = CampaignReflectionLearningRecordCodec.decode(result)
|
|
1352
|
+
if (
|
|
1353
|
+
record.source_generation != request.wave.source_generation
|
|
1354
|
+
or record.source_generation != receipt.source_generation
|
|
1355
|
+
or record.source_stage_receipt_sha256 != request.source_stage.receipt_sha256
|
|
1356
|
+
or record.source_stage_receipt_sha256 != receipt.source_stage_receipt_sha256
|
|
1357
|
+
):
|
|
1358
|
+
raise ValueError("reflection learning record names a foreign source stage")
|
|
1359
|
+
projected: list[CampaignReflectedInsightProjection] = []
|
|
1360
|
+
for draft in record.insights:
|
|
1361
|
+
lineage = record.lineage_for(draft)
|
|
1362
|
+
compiled = self._compile(
|
|
1363
|
+
draft=draft,
|
|
1364
|
+
contract=record.insight_contract,
|
|
1365
|
+
lineage=lineage,
|
|
1366
|
+
)
|
|
1367
|
+
predictions = tuple(
|
|
1368
|
+
HypothesisMetricPrediction(
|
|
1369
|
+
metric_id=value.metric_id,
|
|
1370
|
+
direction=value.direction,
|
|
1371
|
+
)
|
|
1372
|
+
for value in draft.effect_predictions
|
|
1373
|
+
)
|
|
1374
|
+
claim_strength = HypothesisClaimStrength(
|
|
1375
|
+
sufficiency=True,
|
|
1376
|
+
necessity=False,
|
|
1377
|
+
invariance=False,
|
|
1378
|
+
mechanistic_or_causal=(
|
|
1379
|
+
draft.insight_kind is ReflectionInsightKind.MECHANISTIC_CONJECTURE
|
|
1380
|
+
),
|
|
1381
|
+
)
|
|
1382
|
+
template = CampaignSemanticAuditPlanTemplate(
|
|
1383
|
+
trigger=compiled.trigger,
|
|
1384
|
+
intervention=TypedInterventionSignature(
|
|
1385
|
+
affected_paths=draft.affected_paths,
|
|
1386
|
+
old_value_predicate=compiled.old_value_predicate,
|
|
1387
|
+
new_action=compiled.new_action,
|
|
1388
|
+
admissible_operator_families=self.applicable_operator_kinds,
|
|
1389
|
+
),
|
|
1390
|
+
predictions=predictions,
|
|
1391
|
+
claim_strength=claim_strength,
|
|
1392
|
+
scope=self.scope,
|
|
1393
|
+
matcher_definition_sha256=compiled.matcher_definition_sha256,
|
|
1394
|
+
origin_cutoff_event_index=record.origin_cutoff_event_index,
|
|
1395
|
+
minimum_support_clusters=self.minimum_support_clusters,
|
|
1396
|
+
minimum_support_instances=self.minimum_support_instances,
|
|
1397
|
+
)
|
|
1398
|
+
projected.append(
|
|
1399
|
+
CampaignReflectedInsightProjection(
|
|
1400
|
+
draft=draft,
|
|
1401
|
+
evidence_lineage=lineage,
|
|
1402
|
+
semantic_audit_template=template,
|
|
1403
|
+
)
|
|
1404
|
+
)
|
|
1405
|
+
return CampaignReflectionLearningProjection(
|
|
1406
|
+
reflection_request_sha256=request.request_sha256,
|
|
1407
|
+
reflection_receipt_sha256=receipt.receipt_sha256,
|
|
1408
|
+
reflection_result_sha256=typed_json_sha256(result),
|
|
1409
|
+
insights=tuple(projected),
|
|
1410
|
+
applicable_operator_kinds=self.applicable_operator_kinds,
|
|
1411
|
+
diagnostic_operator_kind=self.diagnostic_operator_kind,
|
|
1412
|
+
diagnostic_editable_paths=self.diagnostic_editable_paths,
|
|
1413
|
+
initial_score=self.initial_score,
|
|
1414
|
+
projection_policy_id=self.policy_id,
|
|
1415
|
+
projection_policy_version=self.policy_version,
|
|
1416
|
+
projection_policy_definition_sha256=self.definition_sha256,
|
|
1417
|
+
)
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
@dataclass(frozen=True, slots=True)
|
|
1421
|
+
class CampaignRuntimeReflectionRegistrationReceipt:
|
|
1422
|
+
reflection_receipt_sha256: str
|
|
1423
|
+
reflection_result_sha256: str
|
|
1424
|
+
projection: CampaignReflectionLearningProjection
|
|
1425
|
+
registration: CampaignInsightRegistrationReceipt
|
|
1426
|
+
references: tuple[InsightRef, ...]
|
|
1427
|
+
receipt_sha256: str = field(init=False)
|
|
1428
|
+
|
|
1429
|
+
def __post_init__(self) -> None:
|
|
1430
|
+
require_sha256(self.reflection_receipt_sha256, "reflection_receipt_sha256")
|
|
1431
|
+
require_sha256(self.reflection_result_sha256, "reflection_result_sha256")
|
|
1432
|
+
if type(self.projection) is not CampaignReflectionLearningProjection:
|
|
1433
|
+
raise TypeError("projection must be exact")
|
|
1434
|
+
CampaignReflectionLearningProjection.__post_init__(self.projection)
|
|
1435
|
+
if type(self.registration) is not CampaignInsightRegistrationReceipt:
|
|
1436
|
+
raise TypeError("registration must be exact")
|
|
1437
|
+
CampaignInsightRegistrationReceipt.__post_init__(self.registration)
|
|
1438
|
+
if (
|
|
1439
|
+
type(self.references) is not tuple
|
|
1440
|
+
or not self.references
|
|
1441
|
+
or any(type(value) is not InsightRef for value in self.references)
|
|
1442
|
+
):
|
|
1443
|
+
raise ValueError("references must contain exact InsightRef values")
|
|
1444
|
+
if self.references != tuple(sorted(set(self.references))):
|
|
1445
|
+
raise ValueError("references must be unique and canonical")
|
|
1446
|
+
if (
|
|
1447
|
+
self.reflection_receipt_sha256 != self.projection.reflection_receipt_sha256
|
|
1448
|
+
or self.reflection_result_sha256 != self.projection.reflection_result_sha256
|
|
1449
|
+
or self.references != tuple(value[0] for value in self.registration.entries)
|
|
1450
|
+
):
|
|
1451
|
+
raise ValueError("reflection registration evidence is not joined")
|
|
1452
|
+
object.__setattr__(
|
|
1453
|
+
self,
|
|
1454
|
+
"receipt_sha256",
|
|
1455
|
+
_hash(_REFLECTION_REGISTRATION_DOMAIN, self._unsigned_record()),
|
|
1456
|
+
)
|
|
1457
|
+
|
|
1458
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1459
|
+
return {
|
|
1460
|
+
"schema_version": 1,
|
|
1461
|
+
"reflection_receipt_sha256": self.reflection_receipt_sha256,
|
|
1462
|
+
"reflection_result_sha256": self.reflection_result_sha256,
|
|
1463
|
+
"projection_sha256": self.projection.projection_sha256,
|
|
1464
|
+
"registration_receipt_sha256": self.registration.receipt_sha256,
|
|
1465
|
+
"references": [_reference_record(value) for value in self.references],
|
|
1466
|
+
"lifecycle_state": "quarantined",
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
def to_record(self) -> dict[str, object]:
|
|
1470
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
@dataclass(frozen=True, slots=True)
|
|
1474
|
+
class CampaignDiagnosticExposureReceipt:
|
|
1475
|
+
campaign_admission_request_sha256: str
|
|
1476
|
+
barrier_generation: int
|
|
1477
|
+
reflection_receipt_sha256s: tuple[str, ...]
|
|
1478
|
+
registration_receipt_sha256s: tuple[str, ...]
|
|
1479
|
+
admission: CampaignDiagnosticAdmissionReceipt
|
|
1480
|
+
memory_admission: QuarantineTestAdmissionReceipt
|
|
1481
|
+
references: tuple[InsightRef, ...]
|
|
1482
|
+
receipt_sha256: str = field(init=False)
|
|
1483
|
+
|
|
1484
|
+
def __post_init__(self) -> None:
|
|
1485
|
+
require_sha256(
|
|
1486
|
+
self.campaign_admission_request_sha256,
|
|
1487
|
+
"campaign_admission_request_sha256",
|
|
1488
|
+
)
|
|
1489
|
+
for name in (
|
|
1490
|
+
"reflection_receipt_sha256s",
|
|
1491
|
+
"registration_receipt_sha256s",
|
|
1492
|
+
):
|
|
1493
|
+
values = getattr(self, name)
|
|
1494
|
+
if type(values) is not tuple or not values:
|
|
1495
|
+
raise ValueError(f"{name} must be non-empty")
|
|
1496
|
+
for value in values:
|
|
1497
|
+
require_sha256(value, name)
|
|
1498
|
+
if values != tuple(sorted(set(values))):
|
|
1499
|
+
raise ValueError(f"{name} must be unique and canonical")
|
|
1500
|
+
if type(self.barrier_generation) is not int or self.barrier_generation <= 0:
|
|
1501
|
+
raise ValueError("barrier_generation must be positive")
|
|
1502
|
+
if type(self.admission) is not CampaignDiagnosticAdmissionReceipt:
|
|
1503
|
+
raise TypeError("admission must be exact")
|
|
1504
|
+
CampaignDiagnosticAdmissionReceipt.__post_init__(self.admission)
|
|
1505
|
+
if type(self.memory_admission) is not QuarantineTestAdmissionReceipt:
|
|
1506
|
+
raise TypeError("memory_admission must be exact")
|
|
1507
|
+
QuarantineTestAdmissionReceipt.__post_init__(self.memory_admission)
|
|
1508
|
+
if (
|
|
1509
|
+
type(self.references) is not tuple
|
|
1510
|
+
or not self.references
|
|
1511
|
+
or any(type(value) is not InsightRef for value in self.references)
|
|
1512
|
+
):
|
|
1513
|
+
raise TypeError("references must contain exact InsightRef values")
|
|
1514
|
+
if self.references != tuple(sorted(set(self.references))):
|
|
1515
|
+
raise ValueError("references must be unique and canonical")
|
|
1516
|
+
if (
|
|
1517
|
+
self.admission.campaign_admission_request_sha256
|
|
1518
|
+
!= self.campaign_admission_request_sha256
|
|
1519
|
+
or self.admission.admission_generation != self.barrier_generation
|
|
1520
|
+
or self.admission.references != self.references
|
|
1521
|
+
or self.memory_admission.references != self.references
|
|
1522
|
+
or self.admission.memory_admission_receipt_sha256
|
|
1523
|
+
!= self.memory_admission.receipt_sha256
|
|
1524
|
+
or self.registration_receipt_sha256s
|
|
1525
|
+
!= self.admission.registration_receipt_sha256s
|
|
1526
|
+
):
|
|
1527
|
+
raise ValueError("diagnostic exposure evidence is not joined")
|
|
1528
|
+
object.__setattr__(
|
|
1529
|
+
self,
|
|
1530
|
+
"receipt_sha256",
|
|
1531
|
+
_hash(_DIAGNOSTIC_EXPOSURE_DOMAIN, self._unsigned_record()),
|
|
1532
|
+
)
|
|
1533
|
+
|
|
1534
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1535
|
+
return {
|
|
1536
|
+
"schema_version": 1,
|
|
1537
|
+
"campaign_admission_request_sha256": (
|
|
1538
|
+
self.campaign_admission_request_sha256
|
|
1539
|
+
),
|
|
1540
|
+
"barrier_generation": self.barrier_generation,
|
|
1541
|
+
"reflection_receipt_sha256s": list(self.reflection_receipt_sha256s),
|
|
1542
|
+
"registration_receipt_sha256s": list(self.registration_receipt_sha256s),
|
|
1543
|
+
"campaign_diagnostic_admission_receipt_sha256": (
|
|
1544
|
+
self.admission.receipt_sha256
|
|
1545
|
+
),
|
|
1546
|
+
"memory_admission_receipt_sha256": self.memory_admission.receipt_sha256,
|
|
1547
|
+
"references": [_reference_record(value) for value in self.references],
|
|
1548
|
+
"scope": "controlled_future_testing_only",
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
def to_record(self) -> dict[str, object]:
|
|
1552
|
+
return {**self._unsigned_record(), "receipt_sha256": self.receipt_sha256}
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
@dataclass(frozen=True, slots=True)
|
|
1556
|
+
class _RuntimeGenerationLearningPreparation:
|
|
1557
|
+
audit: CampaignGenerationAuditPreparation
|
|
1558
|
+
learning: CampaignPreparedLearningBarrier | None
|
|
1559
|
+
|
|
1560
|
+
def __post_init__(self) -> None:
|
|
1561
|
+
if type(self.audit) is not CampaignGenerationAuditPreparation:
|
|
1562
|
+
raise TypeError("audit must be exact")
|
|
1563
|
+
CampaignGenerationAuditPreparation.__post_init__(self.audit)
|
|
1564
|
+
if self.learning is not None:
|
|
1565
|
+
if type(self.learning) is not CampaignPreparedLearningBarrier:
|
|
1566
|
+
raise TypeError("learning must be exact or None")
|
|
1567
|
+
CampaignPreparedLearningBarrier.__post_init__(self.learning)
|
|
1568
|
+
projection = self.audit.projection
|
|
1569
|
+
if projection is None:
|
|
1570
|
+
raise ValueError(
|
|
1571
|
+
"learning preparation requires a real audit projection"
|
|
1572
|
+
)
|
|
1573
|
+
if self.learning.audits != projection.audits:
|
|
1574
|
+
raise ValueError("learning preparation differs from real-gate audits")
|
|
1575
|
+
|
|
1576
|
+
|
|
1577
|
+
@dataclass(slots=True)
|
|
1578
|
+
class ClosedLoopCampaignLearningRuntime:
|
|
1579
|
+
"""Real coordinator adapter behind campaign learning lifecycle hooks."""
|
|
1580
|
+
|
|
1581
|
+
learning: ClosedLoopCampaignLearning
|
|
1582
|
+
reflection_projection: CampaignReflectionLearningProjectionPort
|
|
1583
|
+
generation_auditor: TransactionalPortfolioGenerationAuditor
|
|
1584
|
+
_registrations: dict[str, CampaignRuntimeReflectionRegistrationReceipt] = field(
|
|
1585
|
+
init=False, default_factory=dict
|
|
1586
|
+
)
|
|
1587
|
+
_exposures: dict[str, CampaignDiagnosticExposureReceipt] = field(
|
|
1588
|
+
init=False,
|
|
1589
|
+
default_factory=dict,
|
|
1590
|
+
)
|
|
1591
|
+
_runtime_preparations: dict[str, _RuntimeGenerationLearningPreparation] = field(
|
|
1592
|
+
init=False, default_factory=dict
|
|
1593
|
+
)
|
|
1594
|
+
|
|
1595
|
+
def __post_init__(self) -> None:
|
|
1596
|
+
if type(self.learning) is not ClosedLoopCampaignLearning:
|
|
1597
|
+
raise TypeError("learning must be an exact ClosedLoopCampaignLearning")
|
|
1598
|
+
if not isinstance(
|
|
1599
|
+
self.reflection_projection,
|
|
1600
|
+
CampaignReflectionLearningProjectionPort,
|
|
1601
|
+
):
|
|
1602
|
+
raise TypeError("reflection_projection must implement its port")
|
|
1603
|
+
if type(self.generation_auditor) is not TransactionalPortfolioGenerationAuditor:
|
|
1604
|
+
raise TypeError("generation_auditor must be exact")
|
|
1605
|
+
TransactionalPortfolioGenerationAuditor.__post_init__(self.generation_auditor)
|
|
1606
|
+
self._validate_port_identity(self.reflection_projection, "reflection")
|
|
1607
|
+
|
|
1608
|
+
@staticmethod
|
|
1609
|
+
def _validate_port_identity(port: object, name: str) -> None:
|
|
1610
|
+
policy_id = getattr(port, "policy_id", None)
|
|
1611
|
+
policy_version = getattr(port, "policy_version", None)
|
|
1612
|
+
definition_sha256 = getattr(port, "definition_sha256", None)
|
|
1613
|
+
if type(policy_id) is not str or _TOKEN.fullmatch(policy_id) is None:
|
|
1614
|
+
raise ValueError(f"{name} projection policy_id must be canonical")
|
|
1615
|
+
if type(policy_version) is not int or policy_version <= 0:
|
|
1616
|
+
raise ValueError(f"{name} projection policy_version must be positive")
|
|
1617
|
+
require_sha256(definition_sha256, f"{name} projection definition_sha256")
|
|
1618
|
+
|
|
1619
|
+
def reflection_completed(
|
|
1620
|
+
self,
|
|
1621
|
+
request: CampaignReflectionRequest,
|
|
1622
|
+
receipt: CampaignReflectionReceipt,
|
|
1623
|
+
result: FrozenJsonObject,
|
|
1624
|
+
) -> FrozenJsonObject:
|
|
1625
|
+
self.__post_init__()
|
|
1626
|
+
if type(request) is not CampaignReflectionRequest:
|
|
1627
|
+
raise TypeError("request must be exact")
|
|
1628
|
+
CampaignReflectionRequest.__post_init__(request)
|
|
1629
|
+
if type(receipt) is not CampaignReflectionReceipt:
|
|
1630
|
+
raise TypeError("receipt must be exact")
|
|
1631
|
+
CampaignReflectionReceipt.__post_init__(receipt)
|
|
1632
|
+
if type(result) is not FrozenJsonObject or freeze_json(result) is not result:
|
|
1633
|
+
raise TypeError("result must be an exact frozen object")
|
|
1634
|
+
result_sha256 = typed_json_sha256(result)
|
|
1635
|
+
if (
|
|
1636
|
+
receipt.request_sha256 != request.request_sha256
|
|
1637
|
+
or receipt.quarantined_result != result
|
|
1638
|
+
):
|
|
1639
|
+
raise ValueError("reflection receipt differs from request/result")
|
|
1640
|
+
if receipt.receipt_sha256 in self._registrations:
|
|
1641
|
+
raise ValueError("reflection receipt was already registered")
|
|
1642
|
+
projection = self.reflection_projection.project(request, receipt, result)
|
|
1643
|
+
if type(projection) is not CampaignReflectionLearningProjection:
|
|
1644
|
+
raise TypeError("reflection projector returned a foreign value")
|
|
1645
|
+
CampaignReflectionLearningProjection.__post_init__(projection)
|
|
1646
|
+
expected_identity = (
|
|
1647
|
+
self.reflection_projection.policy_id,
|
|
1648
|
+
self.reflection_projection.policy_version,
|
|
1649
|
+
self.reflection_projection.definition_sha256,
|
|
1650
|
+
)
|
|
1651
|
+
observed_identity = (
|
|
1652
|
+
projection.projection_policy_id,
|
|
1653
|
+
projection.projection_policy_version,
|
|
1654
|
+
projection.projection_policy_definition_sha256,
|
|
1655
|
+
)
|
|
1656
|
+
if (
|
|
1657
|
+
projection.reflection_request_sha256 != request.request_sha256
|
|
1658
|
+
or projection.reflection_receipt_sha256 != receipt.receipt_sha256
|
|
1659
|
+
or projection.reflection_result_sha256 != result_sha256
|
|
1660
|
+
or observed_identity != expected_identity
|
|
1661
|
+
):
|
|
1662
|
+
raise ValueError("reflection learning projection is foreign")
|
|
1663
|
+
entries = self.learning.memory.add_reflection_batch(
|
|
1664
|
+
tuple(
|
|
1665
|
+
ReflectedInsightBatchItem(value.draft, value.evidence_lineage)
|
|
1666
|
+
for value in projection.insights
|
|
1667
|
+
),
|
|
1668
|
+
initial_score=projection.initial_score,
|
|
1669
|
+
applicable_operator_kinds=projection.applicable_operator_kinds,
|
|
1670
|
+
)
|
|
1671
|
+
plans = tuple(
|
|
1672
|
+
item.semantic_audit_template.bind(entry)
|
|
1673
|
+
for item, entry in zip(projection.insights, entries, strict=True)
|
|
1674
|
+
)
|
|
1675
|
+
registration = self.learning.register_quarantined_reflections(
|
|
1676
|
+
origin_generation=request.wave.source_generation,
|
|
1677
|
+
references=tuple(entry.reference for entry in entries),
|
|
1678
|
+
semantic_audit_plans=plans,
|
|
1679
|
+
)
|
|
1680
|
+
registered = CampaignRuntimeReflectionRegistrationReceipt(
|
|
1681
|
+
reflection_receipt_sha256=receipt.receipt_sha256,
|
|
1682
|
+
reflection_result_sha256=result_sha256,
|
|
1683
|
+
projection=projection,
|
|
1684
|
+
registration=registration,
|
|
1685
|
+
references=tuple(sorted(entry.reference for entry in entries)),
|
|
1686
|
+
)
|
|
1687
|
+
self._registrations[receipt.receipt_sha256] = registered
|
|
1688
|
+
return _object(registered.to_record())
|
|
1689
|
+
|
|
1690
|
+
def reflections_admitted(
|
|
1691
|
+
self,
|
|
1692
|
+
request: CampaignReflectionTestAdmissionRequest,
|
|
1693
|
+
contents: tuple[
|
|
1694
|
+
tuple[CampaignReflectionReceipt, FrozenJsonObject],
|
|
1695
|
+
...,
|
|
1696
|
+
],
|
|
1697
|
+
) -> FrozenJsonObject:
|
|
1698
|
+
self.__post_init__()
|
|
1699
|
+
if type(request) is not CampaignReflectionTestAdmissionRequest:
|
|
1700
|
+
raise TypeError("request must be exact")
|
|
1701
|
+
CampaignReflectionTestAdmissionRequest.__post_init__(request)
|
|
1702
|
+
if type(contents) is not tuple or not contents:
|
|
1703
|
+
raise ValueError("contents must be a non-empty exact tuple")
|
|
1704
|
+
content_hashes = tuple(value[0].receipt_sha256 for value in contents)
|
|
1705
|
+
if content_hashes != tuple(sorted(set(content_hashes))):
|
|
1706
|
+
raise ValueError("contents must use canonical reflection receipt order")
|
|
1707
|
+
registrations: list[CampaignRuntimeReflectionRegistrationReceipt] = []
|
|
1708
|
+
for reflection, result in contents:
|
|
1709
|
+
if type(reflection) is not CampaignReflectionReceipt:
|
|
1710
|
+
raise TypeError("content reflection receipt must be exact")
|
|
1711
|
+
CampaignReflectionReceipt.__post_init__(reflection)
|
|
1712
|
+
if (
|
|
1713
|
+
type(result) is not FrozenJsonObject
|
|
1714
|
+
or freeze_json(result) is not result
|
|
1715
|
+
):
|
|
1716
|
+
raise TypeError("content result must be frozen")
|
|
1717
|
+
registered = self._registrations.get(reflection.receipt_sha256)
|
|
1718
|
+
if registered is None:
|
|
1719
|
+
raise ValueError("reflection content was not registered")
|
|
1720
|
+
if (
|
|
1721
|
+
registered.reflection_result_sha256 != typed_json_sha256(result)
|
|
1722
|
+
or reflection.quarantined_result != result
|
|
1723
|
+
):
|
|
1724
|
+
raise ValueError("admission content differs from registration")
|
|
1725
|
+
if reflection.receipt_sha256 in self._exposures:
|
|
1726
|
+
raise ValueError("reflection was already admitted")
|
|
1727
|
+
registrations.append(registered)
|
|
1728
|
+
diagnostic_scopes = {
|
|
1729
|
+
(
|
|
1730
|
+
value.projection.diagnostic_operator_kind,
|
|
1731
|
+
value.projection.diagnostic_editable_paths,
|
|
1732
|
+
)
|
|
1733
|
+
for value in registrations
|
|
1734
|
+
}
|
|
1735
|
+
if len(diagnostic_scopes) != 1:
|
|
1736
|
+
raise ValueError(
|
|
1737
|
+
"one atomic reflection barrier requires one diagnostic scope"
|
|
1738
|
+
)
|
|
1739
|
+
operator_kind, editable_paths = next(iter(diagnostic_scopes))
|
|
1740
|
+
references = tuple(
|
|
1741
|
+
sorted(
|
|
1742
|
+
reference
|
|
1743
|
+
for registered in registrations
|
|
1744
|
+
for reference in registered.references
|
|
1745
|
+
)
|
|
1746
|
+
)
|
|
1747
|
+
if len(set(references)) != len(references):
|
|
1748
|
+
raise ValueError("reflection barrier repeats an insight reference")
|
|
1749
|
+
admission = self.learning.admit_for_diagnostic_testing(
|
|
1750
|
+
admission_generation=request.barrier.generation,
|
|
1751
|
+
references=references,
|
|
1752
|
+
campaign_admission_request_sha256=request.request_sha256,
|
|
1753
|
+
operator_kind=operator_kind,
|
|
1754
|
+
editable_paths=editable_paths,
|
|
1755
|
+
)
|
|
1756
|
+
memory_admission = self.learning.memory.quarantine_test_admission_receipt(
|
|
1757
|
+
admission.memory_admission_receipt_sha256
|
|
1758
|
+
)
|
|
1759
|
+
exposure = CampaignDiagnosticExposureReceipt(
|
|
1760
|
+
campaign_admission_request_sha256=request.request_sha256,
|
|
1761
|
+
barrier_generation=request.barrier.generation,
|
|
1762
|
+
reflection_receipt_sha256s=content_hashes,
|
|
1763
|
+
registration_receipt_sha256s=tuple(
|
|
1764
|
+
sorted(value.registration.receipt_sha256 for value in registrations)
|
|
1765
|
+
),
|
|
1766
|
+
admission=admission,
|
|
1767
|
+
memory_admission=memory_admission,
|
|
1768
|
+
references=admission.references,
|
|
1769
|
+
)
|
|
1770
|
+
for reflection_sha256 in content_hashes:
|
|
1771
|
+
self._exposures[reflection_sha256] = exposure
|
|
1772
|
+
return _object(
|
|
1773
|
+
{
|
|
1774
|
+
"schema_version": 1,
|
|
1775
|
+
"campaign_admission_request_sha256": request.request_sha256,
|
|
1776
|
+
"diagnostic_exposures": [exposure.to_record()],
|
|
1777
|
+
"normal_retrieval_mutated": False,
|
|
1778
|
+
}
|
|
1779
|
+
)
|
|
1780
|
+
|
|
1781
|
+
def diagnostic_exposure(
|
|
1782
|
+
self,
|
|
1783
|
+
reflection_receipt_sha256: str,
|
|
1784
|
+
) -> CampaignDiagnosticExposureReceipt:
|
|
1785
|
+
require_sha256(reflection_receipt_sha256, "reflection_receipt_sha256")
|
|
1786
|
+
try:
|
|
1787
|
+
return self._exposures[reflection_receipt_sha256]
|
|
1788
|
+
except KeyError as exc:
|
|
1789
|
+
raise ValueError("reflection has no issued diagnostic exposure") from exc
|
|
1790
|
+
|
|
1791
|
+
def diagnostic_exposures(
|
|
1792
|
+
self,
|
|
1793
|
+
reflection_receipt_sha256s: tuple[str, ...],
|
|
1794
|
+
) -> tuple[CampaignDiagnosticExposureReceipt, ...]:
|
|
1795
|
+
if reflection_receipt_sha256s != tuple(sorted(set(reflection_receipt_sha256s))):
|
|
1796
|
+
raise ValueError("reflection receipt hashes must be unique and canonical")
|
|
1797
|
+
by_receipt = {
|
|
1798
|
+
exposure.receipt_sha256: exposure
|
|
1799
|
+
for exposure in (
|
|
1800
|
+
self.diagnostic_exposure(value) for value in reflection_receipt_sha256s
|
|
1801
|
+
)
|
|
1802
|
+
}
|
|
1803
|
+
return tuple(by_receipt[value] for value in sorted(by_receipt))
|
|
1804
|
+
|
|
1805
|
+
def normal_references(self, **eligibility: object) -> tuple[InsightRef, ...]:
|
|
1806
|
+
"""Expose only the memory bank's normal lifecycle-gated retrieval view."""
|
|
1807
|
+
|
|
1808
|
+
return self.learning.memory.eligible_references(**eligibility)
|
|
1809
|
+
|
|
1810
|
+
def _active_diagnostic_exposures(
|
|
1811
|
+
self,
|
|
1812
|
+
) -> tuple[CampaignDiagnosticExposureReceipt, ...]:
|
|
1813
|
+
active: list[CampaignDiagnosticExposureReceipt] = []
|
|
1814
|
+
for exposure in {
|
|
1815
|
+
value.receipt_sha256: value for value in self._exposures.values()
|
|
1816
|
+
}.values():
|
|
1817
|
+
entries = self.learning.memory.entries_for(exposure.references)
|
|
1818
|
+
if any(
|
|
1819
|
+
entry.lifecycle_state is InsightLifecycleState.QUARANTINED
|
|
1820
|
+
for entry in entries
|
|
1821
|
+
):
|
|
1822
|
+
active.append(exposure)
|
|
1823
|
+
return tuple(sorted(active, key=lambda value: value.receipt_sha256))
|
|
1824
|
+
|
|
1825
|
+
async def prepare_portfolio_generation_close(
|
|
1826
|
+
self,
|
|
1827
|
+
request: CampaignStageRequest,
|
|
1828
|
+
waves: tuple[PortfolioVariationWaveRequest, ...],
|
|
1829
|
+
results: tuple[PortfolioVariationWaveResult, ...],
|
|
1830
|
+
memory_credit_preparation: PortfolioMemoryCreditBatchPreparation,
|
|
1831
|
+
) -> CampaignPortfolioLearningPreparation:
|
|
1832
|
+
"""Implement the runtime's pure prepare half without publishing state."""
|
|
1833
|
+
|
|
1834
|
+
self.__post_init__()
|
|
1835
|
+
if type(request) is not CampaignStageRequest:
|
|
1836
|
+
raise TypeError("request must be exact")
|
|
1837
|
+
CampaignStageRequest.__post_init__(request)
|
|
1838
|
+
if type(memory_credit_preparation) is not (
|
|
1839
|
+
PortfolioMemoryCreditBatchPreparation
|
|
1840
|
+
):
|
|
1841
|
+
raise TypeError("memory_credit_preparation must be exact")
|
|
1842
|
+
PortfolioMemoryCreditBatchPreparation.__post_init__(memory_credit_preparation)
|
|
1843
|
+
if memory_credit_preparation.prepared_results != results:
|
|
1844
|
+
raise ValueError("learning results differ from memory preparation")
|
|
1845
|
+
wave_request_sha256s = tuple(
|
|
1846
|
+
value.selection_request.request_sha256 for value in waves
|
|
1847
|
+
)
|
|
1848
|
+
result_receipt_sha256s = tuple(
|
|
1849
|
+
value.receipt.receipt_sha256 for value in results
|
|
1850
|
+
)
|
|
1851
|
+
active = self._active_diagnostic_exposures()
|
|
1852
|
+
references = tuple(
|
|
1853
|
+
sorted(
|
|
1854
|
+
{
|
|
1855
|
+
entry.reference
|
|
1856
|
+
for exposure in active
|
|
1857
|
+
for entry in self.learning.memory.entries_for(exposure.references)
|
|
1858
|
+
if entry.lifecycle_state is InsightLifecycleState.QUARANTINED
|
|
1859
|
+
}
|
|
1860
|
+
)
|
|
1861
|
+
)
|
|
1862
|
+
entries = self.learning.memory.entries_for(references) if references else ()
|
|
1863
|
+
plans = self.learning.audit_plans_for(references) if references else ()
|
|
1864
|
+
audit_preparation = self.generation_auditor.prepare_generation_audit(
|
|
1865
|
+
request=request,
|
|
1866
|
+
waves=waves,
|
|
1867
|
+
results=results,
|
|
1868
|
+
memory_credit_preparation=memory_credit_preparation,
|
|
1869
|
+
entries=entries,
|
|
1870
|
+
plans=plans,
|
|
1871
|
+
)
|
|
1872
|
+
projection = audit_preparation.projection
|
|
1873
|
+
try:
|
|
1874
|
+
if projection is not None:
|
|
1875
|
+
batch = memory_credit_preparation.batch_receipt
|
|
1876
|
+
if batch is None: # Closed by the auditor; retained defensively.
|
|
1877
|
+
raise AssertionError("real audit projection lost its memory batch")
|
|
1878
|
+
prepared = self.learning.prepare_generation_close(
|
|
1879
|
+
memory_credit_batch=batch,
|
|
1880
|
+
audits=projection.audits,
|
|
1881
|
+
prospective_trials=memory_credit_preparation.expected_trials,
|
|
1882
|
+
)
|
|
1883
|
+
evidence = _object(
|
|
1884
|
+
{
|
|
1885
|
+
"schema_version": 2,
|
|
1886
|
+
"status": "prepared_closed_loop_learning_real_gate",
|
|
1887
|
+
"generation_audit_preparation": (audit_preparation.to_record()),
|
|
1888
|
+
"coordinator_preparation": prepared.to_record(),
|
|
1889
|
+
"lifecycle_publication_deferred": True,
|
|
1890
|
+
}
|
|
1891
|
+
)
|
|
1892
|
+
learning_preparation: CampaignPreparedLearningBarrier | None = prepared
|
|
1893
|
+
else:
|
|
1894
|
+
evidence = _object(
|
|
1895
|
+
{
|
|
1896
|
+
"schema_version": 2,
|
|
1897
|
+
"status": "evidence_append_prepared_no_diagnostic_assignment",
|
|
1898
|
+
"generation_audit_preparation": (audit_preparation.to_record()),
|
|
1899
|
+
"lifecycle_publication_deferred": False,
|
|
1900
|
+
}
|
|
1901
|
+
)
|
|
1902
|
+
learning_preparation = None
|
|
1903
|
+
internal = _RuntimeGenerationLearningPreparation(
|
|
1904
|
+
audit=audit_preparation,
|
|
1905
|
+
learning=learning_preparation,
|
|
1906
|
+
)
|
|
1907
|
+
runtime_preparation = CampaignPortfolioLearningPreparation(
|
|
1908
|
+
request_sha256=request.request_sha256,
|
|
1909
|
+
generation=request.step.generation,
|
|
1910
|
+
wave_request_sha256s=wave_request_sha256s,
|
|
1911
|
+
result_receipt_sha256s=result_receipt_sha256s,
|
|
1912
|
+
memory_credit_preparation_sha256=(
|
|
1913
|
+
memory_credit_preparation.preparation_sha256
|
|
1914
|
+
),
|
|
1915
|
+
evidence=evidence,
|
|
1916
|
+
)
|
|
1917
|
+
if runtime_preparation.preparation_sha256 in self._runtime_preparations:
|
|
1918
|
+
raise ValueError("runtime learning preparation identity collided")
|
|
1919
|
+
except BaseException:
|
|
1920
|
+
self.generation_auditor.abort_generation_audit(audit_preparation)
|
|
1921
|
+
raise
|
|
1922
|
+
self._runtime_preparations[runtime_preparation.preparation_sha256] = internal
|
|
1923
|
+
return runtime_preparation
|
|
1924
|
+
|
|
1925
|
+
def commit_portfolio_generation_close(
|
|
1926
|
+
self,
|
|
1927
|
+
preparation: CampaignPortfolioLearningPreparation,
|
|
1928
|
+
) -> None:
|
|
1929
|
+
"""Publish only the already-sealed coordinator decision after memory."""
|
|
1930
|
+
|
|
1931
|
+
if type(preparation) is not CampaignPortfolioLearningPreparation:
|
|
1932
|
+
raise TypeError("preparation must be exact")
|
|
1933
|
+
CampaignPortfolioLearningPreparation.__post_init__(preparation)
|
|
1934
|
+
if preparation.preparation_sha256 not in self._runtime_preparations:
|
|
1935
|
+
raise ValueError("runtime learning preparation is absent")
|
|
1936
|
+
internal = self._runtime_preparations[preparation.preparation_sha256]
|
|
1937
|
+
if internal.learning is not None:
|
|
1938
|
+
self.learning.commit_generation_close(internal.learning)
|
|
1939
|
+
self.generation_auditor.commit_generation_audit(internal.audit)
|
|
1940
|
+
del self._runtime_preparations[preparation.preparation_sha256]
|
|
1941
|
+
|
|
1942
|
+
def abort_portfolio_generation_close(
|
|
1943
|
+
self,
|
|
1944
|
+
preparation: CampaignPortfolioLearningPreparation,
|
|
1945
|
+
) -> None:
|
|
1946
|
+
"""Discard transient preparation state without touching memory/lifecycle."""
|
|
1947
|
+
|
|
1948
|
+
if type(preparation) is not CampaignPortfolioLearningPreparation:
|
|
1949
|
+
raise TypeError("preparation must be exact")
|
|
1950
|
+
CampaignPortfolioLearningPreparation.__post_init__(preparation)
|
|
1951
|
+
internal = self._runtime_preparations.pop(
|
|
1952
|
+
preparation.preparation_sha256,
|
|
1953
|
+
None,
|
|
1954
|
+
)
|
|
1955
|
+
if internal is not None:
|
|
1956
|
+
self.generation_auditor.abort_generation_audit(internal.audit)
|
|
1957
|
+
|
|
1958
|
+
|
|
1959
|
+
__all__ = [
|
|
1960
|
+
"CAMPAIGN_REFLECTION_LEARNING_RECORD_KEY",
|
|
1961
|
+
"CampaignDiagnosticExposureReceipt",
|
|
1962
|
+
"CampaignGenerationAuditProjection",
|
|
1963
|
+
"CampaignInsightSemanticCompiler",
|
|
1964
|
+
"CampaignReflectedInsightProjection",
|
|
1965
|
+
"CampaignReflectionLearningRecord",
|
|
1966
|
+
"CampaignReflectionLearningRecordCodec",
|
|
1967
|
+
"CampaignReflectionLearningProjection",
|
|
1968
|
+
"CampaignReflectionLearningProjectionPort",
|
|
1969
|
+
"CampaignRuntimeReflectionRegistrationReceipt",
|
|
1970
|
+
"CampaignSemanticAuditPlanTemplate",
|
|
1971
|
+
"ClosedLoopCampaignLearningRuntime",
|
|
1972
|
+
"CompiledCampaignInsightSemantics",
|
|
1973
|
+
"STRUCTURED_REFLECTION_PROJECTION_DEFINITION_SHA256",
|
|
1974
|
+
"STRUCTURED_REFLECTION_PROJECTION_POLICY_ID",
|
|
1975
|
+
"STRUCTURED_REFLECTION_PROJECTION_POLICY_VERSION",
|
|
1976
|
+
"StructuredCampaignReflectionLearningProjector",
|
|
1977
|
+
]
|