animsmith_engine/provenance.rs
1use crate::ResolvedProfile;
2use crate::canonical::project_resolved_profile;
3use animsmith_core::LoadedSource;
4use animsmith_core::engine_contract::EngineContractError;
5use animsmith_core::prediction::{
6 PredictionContractError, PredictionProvenanceV1, RawSourceBindingV1,
7};
8
9/// Failure to project already-resolved engine and same-load source evidence.
10#[derive(Debug, thiserror::Error)]
11#[non_exhaustive]
12pub enum PredictionProvenanceProjectionError {
13 /// The frozen engine profile or its materialized settings contradicted the
14 /// core-owned V1 engine contract.
15 #[error("resolved engine evidence is invalid: {0}")]
16 EngineContract(#[from] EngineContractError),
17 /// The same-load engine, raw-source, or dependency-closure evidence could
18 /// not form one valid prediction-provenance record.
19 #[error("prediction provenance is invalid: {0}")]
20 PredictionContract(#[from] PredictionContractError),
21}
22
23/// Project one resolved engine profile and its same-load source evidence into
24/// the self-contained prediction-provenance V1 contract.
25///
26/// This is a one-way, allocation-only adapter. It does not read source bytes,
27/// resolve a profile or selector again, consult the registry, apply defaults,
28/// or recompute the dependency-closure identity. The caller must pass the
29/// [`LoadedSource`] retained from the same load used to resolve `profile`.
30///
31/// # Errors
32///
33/// Returns [`PredictionProvenanceProjectionError`] if the already-resolved
34/// engine evidence no longer satisfies its core contract or if the source
35/// format and primary identity do not agree across the supplied same-load
36/// evidence.
37pub fn project_prediction_provenance_v1(
38 profile: &ResolvedProfile,
39 source: &LoadedSource,
40) -> Result<PredictionProvenanceV1, PredictionProvenanceProjectionError> {
41 let (profile_contract, settings_contract) = project_resolved_profile(profile)?;
42 let raw_source = RawSourceBindingV1::from_source(source.source_facts());
43 Ok(PredictionProvenanceV1::new(
44 profile_contract,
45 profile.source_format(),
46 settings_contract,
47 raw_source,
48 source.dependency_closure().clone(),
49 )?)
50}