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,246 @@
|
|
|
1
|
+
"""Redaction-before-persistence application service for JSON artifacts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Callable, Optional
|
|
7
|
+
|
|
8
|
+
from agent_evolve.application.event_recorder import EventRecorder
|
|
9
|
+
from agent_evolve.domain.artifact import ArtifactRef, ArtifactRole, artifact_ref_for_bytes
|
|
10
|
+
from agent_evolve.domain.event import ArtifactRegistered, EventEnvelope
|
|
11
|
+
from agent_evolve.domain.ids import CorrelationId, EventId
|
|
12
|
+
from agent_evolve.ports.artifact_sanitizer import ArtifactMinimizer, ArtifactSanitizer
|
|
13
|
+
from agent_evolve.ports.artifact_store import (
|
|
14
|
+
JSON_MEDIA_TYPE,
|
|
15
|
+
ArtifactStore,
|
|
16
|
+
canonical_json_bytes,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ArtifactJournalError(RuntimeError):
|
|
21
|
+
"""Base error for safe preparation or post-write verification failures."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ArtifactPreparationError(ArtifactJournalError):
|
|
25
|
+
"""An input could not be minimized, sanitized, and encoded safely."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ArtifactSizeLimitError(ArtifactPreparationError):
|
|
29
|
+
"""The sanitized canonical representation exceeds the configured limit."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ArtifactPostWriteVerificationError(ArtifactJournalError):
|
|
33
|
+
"""The durable store did not reproduce the complete expected reference."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
AfterArtifactPutHook = Callable[[ArtifactRef], None]
|
|
37
|
+
_PREPARATION_FAILED = object()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True, slots=True)
|
|
41
|
+
class ArtifactRegistrationResult:
|
|
42
|
+
"""Successful durable registration returned by :class:`ArtifactJournal`."""
|
|
43
|
+
|
|
44
|
+
artifact_ref: ArtifactRef
|
|
45
|
+
event: EventEnvelope
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ArtifactJournal:
|
|
49
|
+
"""Prepare, durably store, verify, and then journal a canonical JSON value.
|
|
50
|
+
|
|
51
|
+
Raw caller input is passed only to the in-memory minimizer and sanitizer.
|
|
52
|
+
The sole artifact-store call that writes bytes receives the already
|
|
53
|
+
sanitized canonical encoding.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
*,
|
|
59
|
+
artifact_store: ArtifactStore,
|
|
60
|
+
event_recorder: EventRecorder,
|
|
61
|
+
minimizer: ArtifactMinimizer,
|
|
62
|
+
sanitizer: ArtifactSanitizer,
|
|
63
|
+
max_size_bytes: int,
|
|
64
|
+
after_artifact_put: AfterArtifactPutHook | None = None,
|
|
65
|
+
) -> None:
|
|
66
|
+
if (
|
|
67
|
+
isinstance(max_size_bytes, bool)
|
|
68
|
+
or not isinstance(max_size_bytes, int)
|
|
69
|
+
or max_size_bytes < 1
|
|
70
|
+
):
|
|
71
|
+
raise ValueError("max_size_bytes must be a positive integer")
|
|
72
|
+
self._artifact_store = artifact_store
|
|
73
|
+
self._event_recorder = event_recorder
|
|
74
|
+
self._minimizer = minimizer
|
|
75
|
+
self._sanitizer = sanitizer
|
|
76
|
+
self._max_size_bytes = max_size_bytes
|
|
77
|
+
self._after_artifact_put = after_artifact_put
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def _preparation_failure() -> ArtifactPreparationError:
|
|
81
|
+
# This message intentionally contains no value, field path, match count,
|
|
82
|
+
# or chained exception from a caller-supplied policy implementation.
|
|
83
|
+
return ArtifactPreparationError(
|
|
84
|
+
"artifact preparation failed under the configured safety policies"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def _minimize_without_leaky_context(self, value: Any, role: ArtifactRole) -> Any:
|
|
88
|
+
try:
|
|
89
|
+
return (
|
|
90
|
+
self._minimizer.minimize_json(value, role=role),
|
|
91
|
+
self._minimizer.policy_id,
|
|
92
|
+
self._minimizer.policy_version,
|
|
93
|
+
self._minimizer.policy_config_sha256,
|
|
94
|
+
)
|
|
95
|
+
except Exception:
|
|
96
|
+
# Returning from the handler clears Python's implicit exception
|
|
97
|
+
# context before the caller constructs the public safe error.
|
|
98
|
+
return _PREPARATION_FAILED
|
|
99
|
+
|
|
100
|
+
def _sanitize_without_leaky_context(
|
|
101
|
+
self,
|
|
102
|
+
value: Any,
|
|
103
|
+
role: ArtifactRole,
|
|
104
|
+
) -> Any:
|
|
105
|
+
try:
|
|
106
|
+
sanitized = self._sanitizer.sanitize_json(value, role=role)
|
|
107
|
+
return (
|
|
108
|
+
sanitized,
|
|
109
|
+
self._sanitizer.policy_id,
|
|
110
|
+
self._sanitizer.policy_version,
|
|
111
|
+
)
|
|
112
|
+
except Exception:
|
|
113
|
+
return _PREPARATION_FAILED
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _encode_without_leaky_context(value: Any) -> bytes | object:
|
|
117
|
+
try:
|
|
118
|
+
return canonical_json_bytes(value)
|
|
119
|
+
except Exception:
|
|
120
|
+
return _PREPARATION_FAILED
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _registration_without_leaky_context(
|
|
124
|
+
expected_ref: ArtifactRef,
|
|
125
|
+
role: ArtifactRole,
|
|
126
|
+
minimization_policy_id: Any,
|
|
127
|
+
minimization_policy_version: Any,
|
|
128
|
+
minimization_policy_config_sha256: Any,
|
|
129
|
+
policy_id: Any,
|
|
130
|
+
policy_version: Any,
|
|
131
|
+
) -> ArtifactRegistered | object:
|
|
132
|
+
try:
|
|
133
|
+
return ArtifactRegistered(
|
|
134
|
+
artifact_id=expected_ref.artifact_id,
|
|
135
|
+
sha256_hex=expected_ref.sha256_hex,
|
|
136
|
+
size_bytes=expected_ref.size_bytes,
|
|
137
|
+
media_type=expected_ref.media_type,
|
|
138
|
+
role=role,
|
|
139
|
+
minimization_policy_id=minimization_policy_id,
|
|
140
|
+
minimization_policy_version=minimization_policy_version,
|
|
141
|
+
minimization_policy_config_sha256=(
|
|
142
|
+
minimization_policy_config_sha256
|
|
143
|
+
),
|
|
144
|
+
sanitization_policy_id=policy_id,
|
|
145
|
+
sanitization_policy_version=policy_version,
|
|
146
|
+
)
|
|
147
|
+
except Exception:
|
|
148
|
+
return _PREPARATION_FAILED
|
|
149
|
+
|
|
150
|
+
def register_json(
|
|
151
|
+
self,
|
|
152
|
+
value: Any,
|
|
153
|
+
*,
|
|
154
|
+
role: ArtifactRole,
|
|
155
|
+
correlation_id: Optional[CorrelationId] = None,
|
|
156
|
+
causation_event_id: Optional[EventId] = None,
|
|
157
|
+
) -> ArtifactRegistrationResult:
|
|
158
|
+
"""Register one minimized and sanitized JSON artifact.
|
|
159
|
+
|
|
160
|
+
Ordering is a safety and crash-consistency contract, not an
|
|
161
|
+
implementation detail: minimize -> sanitize -> canonical encode -> size
|
|
162
|
+
gate -> durable put -> full verification -> durable event append.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
if not isinstance(role, ArtifactRole):
|
|
166
|
+
raise TypeError("role must be an ArtifactRole")
|
|
167
|
+
|
|
168
|
+
minimization_result = self._minimize_without_leaky_context(value, role)
|
|
169
|
+
if minimization_result is _PREPARATION_FAILED:
|
|
170
|
+
raise self._preparation_failure()
|
|
171
|
+
(
|
|
172
|
+
minimized,
|
|
173
|
+
minimization_policy_id,
|
|
174
|
+
minimization_policy_version,
|
|
175
|
+
minimization_policy_config_sha256,
|
|
176
|
+
) = minimization_result
|
|
177
|
+
sanitized_result = self._sanitize_without_leaky_context(minimized, role)
|
|
178
|
+
if sanitized_result is _PREPARATION_FAILED:
|
|
179
|
+
raise self._preparation_failure()
|
|
180
|
+
sanitized, policy_id, policy_version = sanitized_result
|
|
181
|
+
encoded = self._encode_without_leaky_context(sanitized)
|
|
182
|
+
if encoded is _PREPARATION_FAILED:
|
|
183
|
+
raise self._preparation_failure()
|
|
184
|
+
assert isinstance(encoded, bytes) # narrowed from the private sentinel union
|
|
185
|
+
|
|
186
|
+
if len(encoded) > self._max_size_bytes:
|
|
187
|
+
raise ArtifactSizeLimitError(
|
|
188
|
+
"sanitized artifact exceeds the configured size limit"
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
expected_ref = artifact_ref_for_bytes(encoded, media_type=JSON_MEDIA_TYPE)
|
|
192
|
+
# Constructing the payload before the write validates all provenance
|
|
193
|
+
# metadata. It is appended only after post-write verification succeeds.
|
|
194
|
+
registration = self._registration_without_leaky_context(
|
|
195
|
+
expected_ref,
|
|
196
|
+
role,
|
|
197
|
+
minimization_policy_id,
|
|
198
|
+
minimization_policy_version,
|
|
199
|
+
minimization_policy_config_sha256,
|
|
200
|
+
policy_id,
|
|
201
|
+
policy_version,
|
|
202
|
+
)
|
|
203
|
+
if registration is _PREPARATION_FAILED:
|
|
204
|
+
raise self._preparation_failure()
|
|
205
|
+
assert isinstance(registration, ArtifactRegistered)
|
|
206
|
+
|
|
207
|
+
persisted_ref = self._artifact_store.put_bytes(
|
|
208
|
+
encoded,
|
|
209
|
+
media_type=JSON_MEDIA_TYPE,
|
|
210
|
+
)
|
|
211
|
+
if self._after_artifact_put is not None:
|
|
212
|
+
# A test/deployment crash hook receives only content metadata, never
|
|
213
|
+
# the raw or sanitized payload. Raising leaves a store-only orphan.
|
|
214
|
+
self._after_artifact_put(expected_ref)
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
stat_ref = self._artifact_store.stat(expected_ref.artifact_id)
|
|
218
|
+
reread = self._artifact_store.read_bytes(
|
|
219
|
+
expected_ref.artifact_id,
|
|
220
|
+
expected_media_type=JSON_MEDIA_TYPE,
|
|
221
|
+
)
|
|
222
|
+
reread_ref = artifact_ref_for_bytes(
|
|
223
|
+
reread,
|
|
224
|
+
media_type=JSON_MEDIA_TYPE,
|
|
225
|
+
)
|
|
226
|
+
except Exception:
|
|
227
|
+
raise ArtifactPostWriteVerificationError(
|
|
228
|
+
"artifact could not be fully verified after persistence"
|
|
229
|
+
) from None
|
|
230
|
+
|
|
231
|
+
if (
|
|
232
|
+
persisted_ref != expected_ref
|
|
233
|
+
or stat_ref != expected_ref
|
|
234
|
+
or reread_ref != expected_ref
|
|
235
|
+
or reread != encoded
|
|
236
|
+
):
|
|
237
|
+
raise ArtifactPostWriteVerificationError(
|
|
238
|
+
"artifact metadata or bytes changed during persistence"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
event = self._event_recorder.record(
|
|
242
|
+
registration,
|
|
243
|
+
correlation_id=correlation_id,
|
|
244
|
+
causation_event_id=causation_event_id,
|
|
245
|
+
)
|
|
246
|
+
return ArtifactRegistrationResult(artifact_ref=expected_ref, event=event)
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""Read-only replay and integrity verification for artifact registrations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import types
|
|
6
|
+
from dataclasses import dataclass, fields
|
|
7
|
+
from typing import Any, Iterable, Optional, Tuple, Union, get_args, get_origin, get_type_hints
|
|
8
|
+
|
|
9
|
+
from agent_evolve.domain.artifact import ArtifactRef, ArtifactRole, artifact_ref_for_bytes
|
|
10
|
+
from agent_evolve.domain.event import (
|
|
11
|
+
EVENT_PAYLOAD_TYPES,
|
|
12
|
+
ArtifactRegistered,
|
|
13
|
+
CandidateProposed,
|
|
14
|
+
EvaluationFailed,
|
|
15
|
+
EventEnvelope,
|
|
16
|
+
EventPayload,
|
|
17
|
+
LLMCallCompleted,
|
|
18
|
+
LLMCallFailed,
|
|
19
|
+
LLMRequestArtifactLinked,
|
|
20
|
+
ProviderAttemptFailed,
|
|
21
|
+
RunAborted,
|
|
22
|
+
RunStarted,
|
|
23
|
+
validated_event_snapshot,
|
|
24
|
+
)
|
|
25
|
+
from agent_evolve.domain.ids import ArtifactId, RunId
|
|
26
|
+
from agent_evolve.ports.artifact_store import (
|
|
27
|
+
JSON_MEDIA_TYPE,
|
|
28
|
+
ArtifactStore,
|
|
29
|
+
canonical_json_bytes,
|
|
30
|
+
decode_json_bytes,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ArtifactReplayError(RuntimeError):
|
|
35
|
+
"""The event stream and artifact store do not form a valid journal."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ArtifactStreamOrderError(ArtifactReplayError):
|
|
39
|
+
"""Replay input is not one contiguous run stream in sequence order."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ArtifactRegistrationIntegrityError(ArtifactReplayError):
|
|
43
|
+
"""A registration claim is inconsistent with the durable store."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class DanglingArtifactReferenceError(ArtifactReplayError):
|
|
47
|
+
"""A semantic reference has no preceding registration."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class IncompatibleArtifactRoleError(ArtifactReplayError):
|
|
51
|
+
"""An artifact was registered earlier, but not under the required role."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ArtifactReferenceSchemaError(ArtifactReplayError):
|
|
55
|
+
"""An ArtifactId-bearing payload field has no explicit role rule."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_SNAPSHOT_FAILED = object()
|
|
59
|
+
_REGISTRATION_VERIFICATION_FAILED = object()
|
|
60
|
+
_REGISTRATION_MISMATCH = object()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True, slots=True)
|
|
64
|
+
class ArtifactRegistrationKey:
|
|
65
|
+
artifact_id: ArtifactId
|
|
66
|
+
role: ArtifactRole
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass(frozen=True, slots=True)
|
|
70
|
+
class ArtifactReplayReport:
|
|
71
|
+
run_id: Optional[RunId]
|
|
72
|
+
event_count: int
|
|
73
|
+
registration_event_count: int
|
|
74
|
+
semantic_reference_count: int
|
|
75
|
+
unique_registration_count: int
|
|
76
|
+
unique_referenced_registration_count: int
|
|
77
|
+
orphan_registrations: Tuple[ArtifactRegistrationKey, ...]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True, slots=True)
|
|
81
|
+
class _RegistrationClaim:
|
|
82
|
+
artifact_ref: ArtifactRef
|
|
83
|
+
minimization_policy_id: str
|
|
84
|
+
minimization_policy_version: str
|
|
85
|
+
minimization_policy_config_sha256: str
|
|
86
|
+
policy_id: str
|
|
87
|
+
policy_version: str
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# The map is intentionally explicit and fail-closed. Adding an ArtifactId field
|
|
91
|
+
# to the event vocabulary requires declaring which semantic role may satisfy it.
|
|
92
|
+
_REFERENCE_ROLES: dict[type[EventPayload], dict[str, ArtifactRole]] = {
|
|
93
|
+
RunStarted: {"manifest_artifact_id": ArtifactRole.RUN_MANIFEST},
|
|
94
|
+
RunAborted: {"diagnostics_artifact_id": ArtifactRole.DIAGNOSTICS},
|
|
95
|
+
LLMCallCompleted: {"response_artifact_id": ArtifactRole.LLM_RESPONSE},
|
|
96
|
+
LLMRequestArtifactLinked: {"request_artifact_id": ArtifactRole.LLM_REQUEST},
|
|
97
|
+
LLMCallFailed: {"diagnostics_artifact_id": ArtifactRole.DIAGNOSTICS},
|
|
98
|
+
ProviderAttemptFailed: {"diagnostics_artifact_id": ArtifactRole.DIAGNOSTICS},
|
|
99
|
+
CandidateProposed: {
|
|
100
|
+
"configuration_artifact_id": ArtifactRole.CANDIDATE_CONFIGURATION
|
|
101
|
+
},
|
|
102
|
+
EvaluationFailed: {"diagnostics_artifact_id": ArtifactRole.DIAGNOSTICS},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _annotation_contains_artifact_id(annotation: Any) -> bool:
|
|
107
|
+
if annotation is ArtifactId:
|
|
108
|
+
return True
|
|
109
|
+
origin = get_origin(annotation)
|
|
110
|
+
if origin in (Union, types.UnionType) or origin in (tuple, list, dict):
|
|
111
|
+
return any(_annotation_contains_artifact_id(arg) for arg in get_args(annotation))
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _annotation_is_scalar_artifact_id(annotation: Any) -> bool:
|
|
116
|
+
if annotation is ArtifactId:
|
|
117
|
+
return True
|
|
118
|
+
origin = get_origin(annotation)
|
|
119
|
+
args = get_args(annotation)
|
|
120
|
+
return origin in (Union, types.UnionType) and set(args) == {
|
|
121
|
+
ArtifactId,
|
|
122
|
+
type(None),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _artifact_fields(payload: EventPayload) -> tuple[str, ...]:
|
|
127
|
+
hints = get_type_hints(type(payload))
|
|
128
|
+
return tuple(
|
|
129
|
+
field.name
|
|
130
|
+
for field in fields(payload)
|
|
131
|
+
if _annotation_contains_artifact_id(hints[field.name])
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def validate_artifact_reference_schema() -> None:
|
|
136
|
+
"""Fail if the complete event vocabulary and semantic role map drift apart."""
|
|
137
|
+
|
|
138
|
+
payload_types = set(EVENT_PAYLOAD_TYPES)
|
|
139
|
+
extra_rule_types = set(_REFERENCE_ROLES) - payload_types
|
|
140
|
+
if extra_rule_types:
|
|
141
|
+
raise ArtifactReferenceSchemaError(
|
|
142
|
+
"artifact reference role rules include an unregistered event payload"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
for payload_type in EVENT_PAYLOAD_TYPES:
|
|
146
|
+
if payload_type is ArtifactRegistered:
|
|
147
|
+
hints = get_type_hints(payload_type)
|
|
148
|
+
registration_artifact_fields = {
|
|
149
|
+
field.name
|
|
150
|
+
for field in fields(payload_type)
|
|
151
|
+
if _annotation_contains_artifact_id(hints[field.name])
|
|
152
|
+
}
|
|
153
|
+
if registration_artifact_fields != {"artifact_id"}:
|
|
154
|
+
raise ArtifactReferenceSchemaError(
|
|
155
|
+
"ArtifactRegistered identity fields require an explicit schema review"
|
|
156
|
+
)
|
|
157
|
+
continue
|
|
158
|
+
hints = get_type_hints(payload_type)
|
|
159
|
+
artifact_fields = {
|
|
160
|
+
field.name
|
|
161
|
+
for field in fields(payload_type)
|
|
162
|
+
if _annotation_contains_artifact_id(hints[field.name])
|
|
163
|
+
}
|
|
164
|
+
declared_roles = _REFERENCE_ROLES.get(payload_type, {})
|
|
165
|
+
if artifact_fields != set(declared_roles):
|
|
166
|
+
raise ArtifactReferenceSchemaError(
|
|
167
|
+
"artifact reference role rules do not match the event schema"
|
|
168
|
+
)
|
|
169
|
+
for field_name in artifact_fields:
|
|
170
|
+
if not _annotation_is_scalar_artifact_id(hints[field_name]):
|
|
171
|
+
raise ArtifactReferenceSchemaError(
|
|
172
|
+
"non-scalar ArtifactId references require an explicit replay design"
|
|
173
|
+
)
|
|
174
|
+
if not isinstance(declared_roles[field_name], ArtifactRole):
|
|
175
|
+
raise ArtifactReferenceSchemaError(
|
|
176
|
+
"artifact reference rules must declare a constrained role"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# Validate the whole vocabulary at import, not only payload types encountered by
|
|
181
|
+
# a particular run. A schema change therefore cannot remain latent in quiet runs.
|
|
182
|
+
validate_artifact_reference_schema()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _try_verify_registration_store(
|
|
186
|
+
registration: ArtifactRegistered,
|
|
187
|
+
store: ArtifactStore,
|
|
188
|
+
) -> ArtifactRef | object:
|
|
189
|
+
expected = registration.artifact_ref
|
|
190
|
+
try:
|
|
191
|
+
stat_ref = store.stat(expected.artifact_id)
|
|
192
|
+
content = store.read_bytes(
|
|
193
|
+
expected.artifact_id,
|
|
194
|
+
expected_media_type=expected.media_type,
|
|
195
|
+
)
|
|
196
|
+
content_ref = artifact_ref_for_bytes(content, media_type=expected.media_type)
|
|
197
|
+
decoded = decode_json_bytes(content)
|
|
198
|
+
canonical_content = canonical_json_bytes(decoded)
|
|
199
|
+
if (
|
|
200
|
+
stat_ref != expected
|
|
201
|
+
or content_ref != expected
|
|
202
|
+
or canonical_content != content
|
|
203
|
+
):
|
|
204
|
+
return _REGISTRATION_MISMATCH
|
|
205
|
+
except Exception:
|
|
206
|
+
# Return from the handler so secret-bearing adapter/codec exceptions do
|
|
207
|
+
# not become context on the public integrity error raised by the caller.
|
|
208
|
+
return _REGISTRATION_VERIFICATION_FAILED
|
|
209
|
+
return expected
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _verify_registration_store(
|
|
213
|
+
registration: ArtifactRegistered,
|
|
214
|
+
store: ArtifactStore,
|
|
215
|
+
) -> ArtifactRef:
|
|
216
|
+
expected = registration.artifact_ref
|
|
217
|
+
if expected.media_type != JSON_MEDIA_TYPE:
|
|
218
|
+
raise ArtifactRegistrationIntegrityError(
|
|
219
|
+
"M1c registrations must use the canonical JSON media type"
|
|
220
|
+
)
|
|
221
|
+
verification = _try_verify_registration_store(registration, store)
|
|
222
|
+
if verification is _REGISTRATION_VERIFICATION_FAILED:
|
|
223
|
+
raise ArtifactRegistrationIntegrityError(
|
|
224
|
+
"a registered artifact could not be verified in the durable store"
|
|
225
|
+
)
|
|
226
|
+
if verification is _REGISTRATION_MISMATCH:
|
|
227
|
+
raise ArtifactRegistrationIntegrityError(
|
|
228
|
+
"a registration does not match a canonical durable artifact reference"
|
|
229
|
+
)
|
|
230
|
+
assert type(verification) is ArtifactRef
|
|
231
|
+
return verification
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def verify_artifact_journal(
|
|
235
|
+
events: Iterable[EventEnvelope],
|
|
236
|
+
*,
|
|
237
|
+
artifact_store: ArtifactStore,
|
|
238
|
+
) -> ArtifactReplayReport:
|
|
239
|
+
"""Verify one run deterministically without mutating events or artifacts.
|
|
240
|
+
|
|
241
|
+
Registrations that are never referenced are valid: they can result from a
|
|
242
|
+
crash after registration but before the semantic event. Conversely, a
|
|
243
|
+
store-only orphan has no journal record and is deliberately invisible here.
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
stream = tuple(validated_event_snapshot(event) for event in events)
|
|
248
|
+
except Exception:
|
|
249
|
+
stream = _SNAPSHOT_FAILED
|
|
250
|
+
if stream is _SNAPSHOT_FAILED:
|
|
251
|
+
raise ArtifactStreamOrderError(
|
|
252
|
+
"artifact replay requires canonically validated event snapshots"
|
|
253
|
+
)
|
|
254
|
+
assert isinstance(stream, tuple)
|
|
255
|
+
|
|
256
|
+
run_id: Optional[RunId] = None
|
|
257
|
+
expected_sequence = 1
|
|
258
|
+
event_count = 0
|
|
259
|
+
registration_event_count = 0
|
|
260
|
+
semantic_reference_count = 0
|
|
261
|
+
registrations: dict[ArtifactRegistrationKey, _RegistrationClaim] = {}
|
|
262
|
+
refs_by_id: dict[ArtifactId, ArtifactRef] = {}
|
|
263
|
+
roles_by_id: dict[ArtifactId, set[ArtifactRole]] = {}
|
|
264
|
+
referenced: set[ArtifactRegistrationKey] = set()
|
|
265
|
+
|
|
266
|
+
for event in stream:
|
|
267
|
+
event_count += 1
|
|
268
|
+
if run_id is None:
|
|
269
|
+
run_id = event.run_id
|
|
270
|
+
elif event.run_id != run_id:
|
|
271
|
+
raise ArtifactStreamOrderError(
|
|
272
|
+
"artifact replay requires events from exactly one run"
|
|
273
|
+
)
|
|
274
|
+
if event.sequence_number != expected_sequence:
|
|
275
|
+
raise ArtifactStreamOrderError(
|
|
276
|
+
"artifact replay requires a contiguous sequence beginning at one"
|
|
277
|
+
)
|
|
278
|
+
expected_sequence += 1
|
|
279
|
+
|
|
280
|
+
payload = event.payload
|
|
281
|
+
if isinstance(payload, ArtifactRegistered):
|
|
282
|
+
registration_event_count += 1
|
|
283
|
+
ref = _verify_registration_store(payload, artifact_store)
|
|
284
|
+
previous_ref = refs_by_id.get(ref.artifact_id)
|
|
285
|
+
if previous_ref is not None and previous_ref != ref:
|
|
286
|
+
raise ArtifactRegistrationIntegrityError(
|
|
287
|
+
"registrations disagree on a complete artifact reference"
|
|
288
|
+
)
|
|
289
|
+
refs_by_id[ref.artifact_id] = ref
|
|
290
|
+
key = ArtifactRegistrationKey(ref.artifact_id, payload.role)
|
|
291
|
+
claim = _RegistrationClaim(
|
|
292
|
+
artifact_ref=ref,
|
|
293
|
+
minimization_policy_id=payload.minimization_policy_id,
|
|
294
|
+
minimization_policy_version=payload.minimization_policy_version,
|
|
295
|
+
minimization_policy_config_sha256=(
|
|
296
|
+
payload.minimization_policy_config_sha256
|
|
297
|
+
),
|
|
298
|
+
policy_id=payload.sanitization_policy_id,
|
|
299
|
+
policy_version=payload.sanitization_policy_version,
|
|
300
|
+
)
|
|
301
|
+
previous_claim = registrations.get(key)
|
|
302
|
+
if previous_claim is not None and previous_claim != claim:
|
|
303
|
+
raise ArtifactRegistrationIntegrityError(
|
|
304
|
+
"duplicate registrations disagree on provenance"
|
|
305
|
+
)
|
|
306
|
+
registrations[key] = claim
|
|
307
|
+
roles_by_id.setdefault(ref.artifact_id, set()).add(payload.role)
|
|
308
|
+
continue
|
|
309
|
+
|
|
310
|
+
artifact_fields = _artifact_fields(payload)
|
|
311
|
+
declared_roles = _REFERENCE_ROLES.get(type(payload), {})
|
|
312
|
+
if set(artifact_fields) != set(declared_roles):
|
|
313
|
+
raise ArtifactReferenceSchemaError(
|
|
314
|
+
"artifact reference role rules do not match the event schema"
|
|
315
|
+
)
|
|
316
|
+
for field_name in artifact_fields:
|
|
317
|
+
artifact_id = getattr(payload, field_name)
|
|
318
|
+
if artifact_id is None:
|
|
319
|
+
continue
|
|
320
|
+
semantic_reference_count += 1
|
|
321
|
+
role = declared_roles[field_name]
|
|
322
|
+
key = ArtifactRegistrationKey(artifact_id, role)
|
|
323
|
+
if key not in registrations:
|
|
324
|
+
if artifact_id in roles_by_id:
|
|
325
|
+
raise IncompatibleArtifactRoleError(
|
|
326
|
+
"an artifact reference has no preceding compatible role"
|
|
327
|
+
)
|
|
328
|
+
raise DanglingArtifactReferenceError(
|
|
329
|
+
"an artifact reference has no preceding registration"
|
|
330
|
+
)
|
|
331
|
+
referenced.add(key)
|
|
332
|
+
|
|
333
|
+
orphan_keys = tuple(
|
|
334
|
+
sorted(
|
|
335
|
+
set(registrations) - referenced,
|
|
336
|
+
key=lambda key: (key.artifact_id.value, key.role.value),
|
|
337
|
+
)
|
|
338
|
+
)
|
|
339
|
+
return ArtifactReplayReport(
|
|
340
|
+
run_id=run_id,
|
|
341
|
+
event_count=event_count,
|
|
342
|
+
registration_event_count=registration_event_count,
|
|
343
|
+
semantic_reference_count=semantic_reference_count,
|
|
344
|
+
unique_registration_count=len(registrations),
|
|
345
|
+
unique_referenced_registration_count=len(referenced),
|
|
346
|
+
orphan_registrations=orphan_keys,
|
|
347
|
+
)
|