latent-inspector 0.1.0

Fast CLI for inspecting and comparing learned representations across self-supervised vision models
Documentation
use crate::models::registry::RegistryEntry;
use crate::models::InferenceBackend;
use crate::validation::fixtures::{
    build_reference_artifact_id, ContractArtifact, LoadedFixtureSet, ReferenceArtifact,
};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FreshnessReport {
    reasons: Vec<String>,
}

impl FreshnessReport {
    pub fn new(reasons: Vec<String>) -> Self {
        Self { reasons }
    }

    pub fn is_stale(&self) -> bool {
        !self.reasons.is_empty()
    }

    pub fn reasons(&self) -> &[String] {
        &self.reasons
    }
}

pub fn preprocess_evidence_freshness(
    entry: &RegistryEntry,
    contract: &ContractArtifact,
    fixture_set: &LoadedFixtureSet,
) -> FreshnessReport {
    let mut reasons = shared_profile_reasons(
        entry,
        &contract.model,
        &contract.profile.source,
        &contract.profile.fixture_set,
        &contract.profile.evidence_timestamp,
        fixture_set,
    );

    if contract.profile.preprocess != entry.validation.preprocess {
        reasons.push(
            "approved preprocessing evidence no longer matches the current registry contract"
                .to_string(),
        );
    }

    FreshnessReport::new(reasons)
}

pub fn tensor_evidence_freshness(
    entry: &RegistryEntry,
    contract: &ContractArtifact,
    fixture_set: &LoadedFixtureSet,
) -> FreshnessReport {
    let mut reasons = shared_profile_reasons(
        entry,
        &contract.model,
        &contract.profile.source,
        &contract.profile.fixture_set,
        &contract.profile.evidence_timestamp,
        fixture_set,
    );

    if contract.profile.tensor != entry.validation.tensor {
        reasons.push(
            "approved tensor semantics evidence no longer matches the current registry contract"
                .to_string(),
        );
    }

    FreshnessReport::new(reasons)
}

pub fn parity_evidence_freshness(
    entry: &RegistryEntry,
    reference: &ReferenceArtifact,
    fixture_set: &LoadedFixtureSet,
) -> FreshnessReport {
    let mut reasons = shared_profile_reasons(
        entry,
        &reference.model,
        &reference.source,
        &reference.fixture_set,
        &reference.evidence_timestamp,
        fixture_set,
    );

    let expected_artifact_id = build_reference_artifact_id(
        &entry.info.name,
        &entry.validation.fixture_set,
        &entry.validation.evidence_timestamp,
    );
    if reference.artifact_id != expected_artifact_id {
        reasons.push(format!(
            "reference artifact id '{}' does not match the current approved identity '{}'",
            reference.artifact_id, expected_artifact_id
        ));
    }

    if reference.tolerances != entry.validation.tolerances {
        reasons.push(
            "approved parity tolerances no longer match the current registry contract".to_string(),
        );
    }

    let expected_backend = if entry.is_ready() {
        InferenceBackend::OnnxRuntime
    } else {
        InferenceBackend::Stub
    };
    if reference.backend != expected_backend {
        reasons.push(format!(
            "reference backend '{}' does not match the expected '{}' execution path for this model",
            reference.backend, expected_backend
        ));
    }

    FreshnessReport::new(reasons)
}

fn shared_profile_reasons(
    entry: &RegistryEntry,
    artifact_model: &str,
    artifact_source: &str,
    artifact_fixture_set: &str,
    artifact_timestamp: &str,
    fixture_set: &LoadedFixtureSet,
) -> Vec<String> {
    let expected = &entry.validation;
    let mut reasons = Vec::new();

    if artifact_model != entry.info.name {
        reasons.push(format!(
            "artifact model '{}' does not match the requested model '{}'",
            artifact_model, entry.info.name
        ));
    }

    if artifact_source != expected.source {
        reasons.push(format!(
            "artifact source '{}' does not match the current registry source '{}'",
            artifact_source, expected.source
        ));
    }

    if artifact_fixture_set != expected.fixture_set {
        reasons.push(format!(
            "artifact fixture set '{}' does not match the current registry fixture set '{}'",
            artifact_fixture_set, expected.fixture_set
        ));
    }

    if fixture_set.manifest.fixture_set != expected.fixture_set {
        reasons.push(format!(
            "fixture manifest '{}' does not match the current registry fixture set '{}'",
            fixture_set.manifest.fixture_set, expected.fixture_set
        ));
    }

    if artifact_timestamp != expected.evidence_timestamp {
        reasons.push(format!(
            "artifact evidence timestamp '{}' does not match the current registry timestamp '{}'",
            artifact_timestamp, expected.evidence_timestamp
        ));
    }

    if fixture_set.manifest.evidence_timestamp != expected.evidence_timestamp {
        reasons.push(format!(
            "fixture manifest timestamp '{}' does not match the current registry timestamp '{}'",
            fixture_set.manifest.evidence_timestamp, expected.evidence_timestamp
        ));
    }

    reasons
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::models::registry;
    use crate::validation::fixtures::load_fixture_set;

    #[test]
    fn contract_freshness_flags_outdated_preprocess_evidence() {
        let fixture_set = load_fixture_set(None).unwrap();
        let entry = registry::find("dinov2-vit-l14").unwrap();
        let mut contract = fixture_set.load_contract("dinov2-vit-l14").unwrap();
        contract.profile.evidence_timestamp = "2026-03-28T00:00:00Z".to_string();

        let freshness = preprocess_evidence_freshness(&entry, &contract, &fixture_set);
        assert!(freshness.is_stale());
        assert!(freshness
            .reasons()
            .iter()
            .any(|reason| reason.contains("artifact evidence timestamp")));
    }

    #[test]
    fn tensor_freshness_flags_contract_tensor_drift() {
        let fixture_set = load_fixture_set(None).unwrap();
        let entry = registry::find("dinov2-vit-l14").unwrap();
        let mut contract = fixture_set.load_contract("dinov2-vit-l14").unwrap();
        contract.profile.tensor.embedding_dim += 1;

        let freshness = tensor_evidence_freshness(&entry, &contract, &fixture_set);
        assert!(freshness.is_stale());
        assert!(freshness
            .reasons()
            .iter()
            .any(|reason| reason.contains("tensor semantics evidence")));
    }

    #[test]
    fn parity_freshness_flags_reference_identity_drift() {
        let fixture_set = load_fixture_set(None).unwrap();
        let entry = registry::find("dinov2-vit-l14").unwrap();
        let mut reference = fixture_set.load_reference("dinov2-vit-l14").unwrap();
        reference.artifact_id = "unexpected".to_string();

        let freshness = parity_evidence_freshness(&entry, &reference, &fixture_set);
        assert!(freshness.is_stale());
        assert!(freshness
            .reasons()
            .iter()
            .any(|reason| reason.contains("reference artifact id")));
    }

    #[test]
    fn parity_freshness_flags_reference_backend_drift() {
        let fixture_set = load_fixture_set(None).unwrap();
        let entry = registry::find("dinov2-vit-l14").unwrap();
        let mut reference = fixture_set.load_reference("dinov2-vit-l14").unwrap();
        reference.backend = InferenceBackend::Stub;

        let freshness = parity_evidence_freshness(&entry, &reference, &fixture_set);
        assert!(freshness.is_stale());
        assert!(freshness
            .reasons()
            .iter()
            .any(|reason| reason.contains("reference backend")));
    }
}