Skip to main content

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}