kernel-conformance 0.1.0

Conformance harness for recursive inference kernel authority, compiler, and oracle gates
Documentation
#![allow(clippy::expect_used)]
#![allow(deprecated)]

use constraint_compiler::{compile_batch, CompilerPolicy};
use forge_engine::experiment::{EffectKind, TypedLocatedEffect};
use forge_engine::export::EpisodeExport;
use forge_engine::lab::evaluate::ScoreVector;
use forge_engine::{CausalHypothesis, ClaimStrength, ExperimentEvidenceBundle, HypothesisStatus};
use forge_memory_bridge::transform_envelope_v3;
use knowledge_runtime::{
    adapters::semantic_memory::SemanticMemoryAdapter, config::ProjectionConfig, KnowledgeRuntime,
    RuntimeConfig, Scope,
};
use proptest::prelude::*;
use semantic_memory::{MemoryConfig, MemoryStore, MockEmbedder};
use semantic_memory_forge::{
    ExportClaim, ExportEnvelopeV1, ExportEnvelopeV2, ExportEnvelopeV3, ExportRecord,
    EXPORT_ENVELOPE_V1_SCHEMA,
};
use stack_ids::{EntityId, EnvelopeId, ScopeKey};
use tempfile::TempDir;

fn open_store(base_dir: &std::path::Path) -> MemoryStore {
    let config = MemoryConfig {
        base_dir: base_dir.to_path_buf(),
        ..Default::default()
    };
    let embedder = Box::new(MockEmbedder::new(config.embedding.dimensions));
    MemoryStore::open_with_embedder(config, embedder).unwrap()
}

fn runtime_for_store(store: MemoryStore, namespace: &str) -> KnowledgeRuntime {
    let adapter = SemanticMemoryAdapter::new(store);
    KnowledgeRuntime::new(
        RuntimeConfig {
            default_scope: Scope::new(namespace),
            query: Default::default(),
            entity: Default::default(),
            projection: ProjectionConfig {
                staleness_threshold_secs: 3600,
                import_staleness_threshold_secs: 0,
                persist: false,
            },
            strict_temporal: false,
            strict_scope: false,
        },
        adapter,
    )
    .unwrap()
}

fn canonical_bundle(bundle_id: &str, effect_count: usize) -> ExperimentEvidenceBundle {
    let all_effects = (0..effect_count)
        .map(|index| TypedLocatedEffect {
            kind: EffectKind::TestFailure,
            file: Some(std::path::PathBuf::from(format!("src/property_{index}.rs"))),
            line: Some(10 + index as u32),
            message: format!("property effect {index}"),
            in_baseline: false,
            in_patched: true,
        })
        .collect();

    ExperimentEvidenceBundle {
        bundle_id: bundle_id.into(),
        candidate_id: format!("candidate-{bundle_id}"),
        eval_id: format!("eval-{bundle_id}"),
        version_id: "v0001".into(),
        scores: ScoreVector {
            correctness: 0.9,
            novelty: 0.2,
            stability: 0.8,
            weighted_total: 0.85,
            cea_confidence: None,
            cea_predicted_correctness: None,
        },
        hypotheses: vec![CausalHypothesis {
            hypothesis_id: format!("hypothesis-{bundle_id}"),
            cause_signature: "cause".into(),
            effect_signature: "effect".into(),
            confidence: 0.7,
            status: HypothesisStatus::Proposed,
            support_count: 0,
            contradiction_count: 0,
        }],
        verification: None,
        trace_id: Some(format!("trace-{bundle_id}")),
        experiment_diff: None,
        attribution_json: None,
        assessment: None,
        warnings: vec![],
        created_at: "2026-03-11T00:00:00Z".into(),
        run_id: Some(format!("run-{bundle_id}")),
        attempt_id: Some(format!("attempt-{bundle_id}")),
        supersedes_claim_version_id: None,
        relation_lineage_hints: Default::default(),
        causal_question: None,
        unit_definition: None,
        bundle_scope: None,
        pair_comparability: None,
        claim_strength: ClaimStrength::ProvisionalSinglePair,
        identification_rationale: None,
        known_threats: vec![],
        patch_hash: None,
        treatment: None,
        outcome: Some("verified".into()),
        covariates: None,
        promotion_state: None,
        primary_effect: None,
        all_effects,
        hypothesis_edges: vec![],
        receipts: vec![],
        verification_trials: vec![],
        refutation_artifacts: vec![],
        sealed: false,
    }
}

fn permutation(choice: u8) -> [usize; 3] {
    match choice % 6 {
        0 => [0, 1, 2],
        1 => [0, 2, 1],
        2 => [1, 0, 2],
        3 => [1, 2, 0],
        4 => [2, 0, 1],
        _ => [2, 1, 0],
    }
}

fn base_records() -> Vec<ExportRecord> {
    vec![
        ExportRecord::Claim(ExportClaim {
            claim_id: None,
            claim_version_id: Some("claim-version-order-1".into()),
            subject_entity_id: EntityId::new("entity-order-1"),
            predicate: "supports".into(),
            object_anchor: serde_json::json!("result-a"),
            valid_from: None,
            valid_to: None,
            confidence: 1.0,
            content: "first".into(),
            projection_family: "forge_verification".into(),
            supersedes_claim_id: None,
            supersedes_claim_version_id: None,
            metadata: None,
        }),
        ExportRecord::Claim(ExportClaim {
            claim_id: None,
            claim_version_id: Some("claim-version-order-2".into()),
            subject_entity_id: EntityId::new("entity-order-2"),
            predicate: "supports".into(),
            object_anchor: serde_json::json!("result-b"),
            valid_from: None,
            valid_to: None,
            confidence: 1.0,
            content: "second".into(),
            projection_family: "forge_verification".into(),
            supersedes_claim_id: None,
            supersedes_claim_version_id: None,
            metadata: None,
        }),
        ExportRecord::Claim(ExportClaim {
            claim_id: None,
            claim_version_id: Some("claim-version-order-3".into()),
            subject_entity_id: EntityId::new("entity-order-3"),
            predicate: "supports".into(),
            object_anchor: serde_json::json!("result-c"),
            valid_from: None,
            valid_to: None,
            confidence: 1.0,
            content: "third".into(),
            projection_family: "forge_verification".into(),
            supersedes_claim_id: None,
            supersedes_claim_version_id: None,
            metadata: None,
        }),
    ]
}

fn envelope_for_permutation(choice: u8) -> ExportEnvelopeV1 {
    let records = base_records();
    let order = permutation(choice);
    let reordered = order
        .into_iter()
        .map(|idx| records[idx].clone())
        .collect::<Vec<_>>();
    let scope_key = ScopeKey::namespace_only("property-order");
    let digest = ExportEnvelopeV1::compute_digest("forge", &scope_key, &reordered).unwrap();

    ExportEnvelopeV1 {
        envelope_id: EnvelopeId::new(format!("env-order-{}", choice % 6)),
        schema_version: EXPORT_ENVELOPE_V1_SCHEMA.into(),
        content_digest: digest,
        source_authority: "forge".into(),
        scope_key,
        trace_ctx: None,
        exported_at: "2026-03-11T00:00:00Z".into(),
        records: reordered,
    }
}

fn compiled_signature(
    output: &constraint_compiler::CompileOutput,
) -> (Vec<String>, Vec<String>, Vec<String>) {
    let mut nodes = output
        .nodes
        .iter()
        .map(|node| node.node_id.clone())
        .collect::<Vec<_>>();
    let mut edges = output
        .hyperedges
        .iter()
        .map(|edge| edge.edge_id.clone())
        .collect::<Vec<_>>();
    let mut constraints = output
        .constraints
        .iter()
        .map(|constraint| constraint.constraint_id.as_str().to_string())
        .collect::<Vec<_>>();
    nodes.sort();
    edges.sort();
    constraints.sort();
    (nodes, edges, constraints)
}

proptest! {
    #[test]
    fn compiler_signature_is_stable_under_record_reordering(choice in 0u8..6) {
        let baseline = envelope_for_permutation(0);
        let candidate = envelope_for_permutation(choice);
        let baseline_batch = transform_envelope_v3(
            &ExportEnvelopeV3::try_from(ExportEnvelopeV2::from(baseline)).unwrap(),
        ).unwrap();
        let candidate_batch = transform_envelope_v3(
            &ExportEnvelopeV3::try_from(ExportEnvelopeV2::from(candidate)).unwrap(),
        ).unwrap();

        let policy = CompilerPolicy {
            policy_version: "kernel-conformance.property.order".into(),
            include_hyperedges: true,
        };

        prop_assert_eq!(
            compiled_signature(&compile_batch(&baseline_batch, &policy)),
            compiled_signature(&compile_batch(&candidate_batch, &policy)),
        );
    }

    #[test]
    fn living_memory_thin_export_degradation_is_explicit(effect_count in 1usize..6) {
        let runtime = tokio::runtime::Runtime::new().unwrap();
        runtime.block_on(async move {
            let dir = TempDir::new().unwrap();
            let bundle = canonical_bundle("property-thin", effect_count);
            let export = EpisodeExport::from_bundle(&bundle, "property-thin");
            let envelope = export.to_export_envelope_v3(&bundle).unwrap();
            let batch = transform_envelope_v3(&envelope).unwrap();
            let memory = open_store(dir.path());

            let imported = memory.import_projection_batch(&batch).await.unwrap();
            prop_assert_eq!(imported.status, "complete");

            let runtime = runtime_for_store(memory, "property-thin");
            let advisory = runtime
                .latest_inference_advisory(Some(&Scope::new("property-thin")))
                .await
                .unwrap()
                .expect("expected advisory from imported batch");

            prop_assert!(advisory
                .degradation_markers
                .contains(&"thin_export".to_string()));
            Ok(())
        })?;
    }
}