bijux-dag-runtime 0.4.1

Execution engine, replay semantics, and runtime policy layer for Bijux DAG graphs.
Documentation
//! Runtime cache models and helpers.
#![allow(unused_imports)]

pub(crate) mod key;
pub(crate) mod lineage;
pub(crate) mod proof;
pub(crate) mod store;

pub use crate::CacheMode;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

pub const CACHE_METADATA_VERSION: &str = "cache-meta/v0.4";
pub const CACHE_METADATA_VERSION_PREVIOUS: &str = "cache-meta/v0.3";
pub const CACHE_METADATA_VERSION_LEGACY: &str = "cache-meta/v0.2";
pub const CACHE_ENTRY_MANIFEST_VERSION: &str = "cache-entry/v0.1";

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheKeyInput {
    pub execution_fingerprint: String,
    pub node_definition_fingerprint: String,
    pub declared_environment_fingerprint: String,
    pub input_lineage_fingerprint: String,
    pub adapter_id: String,
    pub adapter_version: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub adapter_binary_sha256: Option<String>,
    pub output_schema_version: String,
    pub policy_fingerprint: String,
    pub execution_contract_fingerprint: String,
    pub backend_class: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheKeyExplanation {
    pub key: String,
    pub intentional_inputs: Vec<(String, String)>,
    pub accidental_inputs: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheManifestOutput {
    pub name: String,
    pub path: String,
    pub kind: String,
    pub media_type: String,
    pub required: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheEntryManifest {
    pub manifest_version: String,
    pub cache_key: String,
    pub node_id: String,
    pub outputs: Vec<CacheManifestOutput>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheExplainabilityProof {
    pub params_fingerprint: String,
    pub command_fingerprint: Option<String>,
}

pub fn cache_key_explanation(input: &CacheKeyInput) -> CacheKeyExplanation {
    let intentional_inputs = vec![
        ("node_definition_fingerprint".to_string(), input.node_definition_fingerprint.clone()),
        (
            "declared_environment_fingerprint".to_string(),
            input.declared_environment_fingerprint.clone(),
        ),
        ("input_lineage_fingerprint".to_string(), input.input_lineage_fingerprint.clone()),
        ("adapter_id".to_string(), input.adapter_id.clone()),
        ("adapter_version".to_string(), input.adapter_version.clone()),
        (
            "adapter_binary_sha256".to_string(),
            input.adapter_binary_sha256.clone().unwrap_or_default(),
        ),
        ("output_schema_version".to_string(), input.output_schema_version.clone()),
        ("policy_fingerprint".to_string(), input.policy_fingerprint.clone()),
        (
            "execution_contract_fingerprint".to_string(),
            input.execution_contract_fingerprint.clone(),
        ),
        ("backend_class".to_string(), input.backend_class.clone()),
    ];
    let mut hasher = Sha256::new();
    for (k, v) in &intentional_inputs {
        hasher.update(k.as_bytes());
        hasher.update(b"=");
        hasher.update(v.as_bytes());
        hasher.update(b";");
    }
    let key = hex::encode(hasher.finalize());
    CacheKeyExplanation { key, intentional_inputs, accidental_inputs: Vec::new() }
}

pub fn cache_key_input_from_meta(meta: &serde_json::Value) -> Option<CacheKeyInput> {
    Some(CacheKeyInput {
        execution_fingerprint: meta.get("node_fingerprint").and_then(|v| v.as_str())?.to_string(),
        node_definition_fingerprint: meta
            .get("node_definition_fingerprint")
            .and_then(|v| v.as_str())?
            .to_string(),
        declared_environment_fingerprint: meta
            .get("declared_environment_fingerprint")
            .and_then(|v| v.as_str())?
            .to_string(),
        input_lineage_fingerprint: meta
            .get("input_lineage_fingerprint")
            .and_then(|v| v.as_str())?
            .to_string(),
        adapter_id: meta.get("adapter_id").and_then(|v| v.as_str())?.to_string(),
        adapter_version: meta.get("adapter_version").and_then(|v| v.as_str())?.to_string(),
        adapter_binary_sha256: meta
            .get("adapter_binary_sha256")
            .and_then(|v| v.as_str())
            .map(ToString::to_string),
        output_schema_version: meta
            .get("produces_outputs_schema_version")
            .or_else(|| meta.get("output_schema_version"))
            .and_then(|v| v.as_str())?
            .to_string(),
        policy_fingerprint: meta.get("policy_fingerprint").and_then(|v| v.as_str())?.to_string(),
        execution_contract_fingerprint: meta
            .get("execution_contract_fingerprint")
            .and_then(|v| v.as_str())?
            .to_string(),
        backend_class: meta.get("backend_class").and_then(|v| v.as_str())?.to_string(),
    })
}

pub fn cache_entry_has_required_proof(meta: &serde_json::Value) -> bool {
    meta.get("cache_key").and_then(|v| v.as_str()).is_some()
        && cache_key_input_from_meta(meta).is_some()
}

pub fn cache_explainability_proof_from_meta(
    meta: &serde_json::Value,
) -> Option<CacheExplainabilityProof> {
    Some(CacheExplainabilityProof {
        params_fingerprint: meta.get("params_fingerprint").and_then(|v| v.as_str())?.to_string(),
        command_fingerprint: meta
            .get("command_fingerprint")
            .and_then(|v| v.as_str())
            .map(ToString::to_string),
    })
}

pub fn cache_metadata_version_supported(meta: &serde_json::Value) -> bool {
    meta.get("cache_metadata_version")
        .and_then(|v| v.as_str())
        .map(|version| {
            version == CACHE_METADATA_VERSION
                || version == CACHE_METADATA_VERSION_PREVIOUS
                || version == CACHE_METADATA_VERSION_LEGACY
        })
        .unwrap_or(false)
}

pub fn cache_entry_manifest_version_supported(manifest: &CacheEntryManifest) -> bool {
    manifest.manifest_version == CACHE_ENTRY_MANIFEST_VERSION
}