vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use super::{
    certify, compute_registry_hash, to_human, to_json, with_test_mutation_catalog,
    CertificateStrength, ConformanceLevel, EngineStatus, OpOutcome,
};
use std::sync::atomic::{AtomicUsize, Ordering};
use vyre::{BackendError, DispatchConfig, Program, VyreBackend};
use vyre_spec::Category;

struct CpuMirrorBackend;

impl VyreBackend for CpuMirrorBackend {
    fn id(&self) -> &'static str {
        "cpu-mirror"
    }

    fn dispatch(
        &self,
        program: &Program,
        inputs: &[Vec<u8>],
        _config: &DispatchConfig,
    ) -> Result<Vec<Vec<u8>>, BackendError> {
        let input = inputs.first().map(Vec::as_slice).unwrap_or(&[]);
        let output = match program.entry_op_id.as_deref() {
            Some("primitive.bitwise.xor") => crate::spec::primitive::xor::cpu_fn()(input),
            Some("primitive.bitwise.and") => crate::spec::primitive::and::cpu_fn()(input),
            Some(op_id) => {
                return Err(BackendError::new(format!(
                    "unsupported test op `{op_id}`. Fix: add an in-memory CPU mirror for this op."
                )))
            }
            None => {
                return Err(BackendError::new(
                    "program missing entry_op_id. Fix: build certification programs through framework::program.",
                ))
            }
        };
        Ok(vec![output])
    }
}

struct UnsupportedBackend;

impl VyreBackend for UnsupportedBackend {
    fn id(&self) -> &'static str {
        "unsupported-test"
    }

    fn dispatch(
        &self,
        _program: &Program,
        _inputs: &[Vec<u8>],
        _config: &DispatchConfig,
    ) -> Result<Vec<Vec<u8>>, BackendError> {
        Err(BackendError::new(
            "UnsupportedByBackend { op: primitive.bitwise.xor, backend: unsupported-test }"
                .to_string(),
        ))
    }
}

struct FlippingBackend {
    calls: AtomicUsize,
}

impl VyreBackend for FlippingBackend {
    fn id(&self) -> &'static str {
        "flipping-test"
    }

    fn dispatch(
        &self,
        _program: &Program,
        inputs: &[Vec<u8>],
        _config: &DispatchConfig,
    ) -> Result<Vec<Vec<u8>>, BackendError> {
        let input = inputs.first().map(Vec::as_slice).unwrap_or(&[]);
        let mut output = crate::spec::primitive::xor::cpu_fn()(input);
        if self.calls.fetch_add(1, Ordering::SeqCst) % 2 == 1 {
            output[0] ^= 1;
        }
        Ok(vec![output])
    }
}

#[test]
fn unsupported_backend_dispatch_records_unsupported_outcome() {
    let mut spec = crate::spec::primitive::xor::spec();
    spec.category = Category::C {
        hardware: "test.intrinsic",
        backend_availability: |_| true,
    };

    let cert = certify(&UnsupportedBackend, &[spec], CertificateStrength::FastCheck).unwrap();

    assert_eq!(cert.ops[0].outcome, OpOutcome::Unsupported);
    assert!(!cert.ops[0].parity_passed);
    assert_eq!(cert.unsupported_ops, vec!["primitive.bitwise.xor"]);
    assert_eq!(cert.integer_track.unsupported_ops, cert.unsupported_ops);
    assert_eq!(cert.coverage.ops_unsupported, 1);
    assert_eq!(cert.coverage.ops_parity_passed, 0);
    assert_eq!(cert.integer_track.level, None);
}

#[test]
fn duplicate_op_ids_are_rejected_before_certification() {
    let spec = crate::spec::primitive::xor::spec();
    let err = certify(
        &CpuMirrorBackend,
        &[spec.clone(), spec],
        CertificateStrength::FastCheck,
    )
    .unwrap_err();
    assert!(err.contains("duplicate op id"), "got: {err}");
}

#[test]
fn registry_hash_changes_when_kernel_wgsl_changes() {
    // P1.20-F3: registry_hash must bind to the actual op source, not just
    // (id, version). A contributor swapping the WGSL kernel after cert
    // (T43 certificate replay) must produce a different hash so the cert
    // becomes detectably invalid.
    let mut spec = crate::spec::primitive::xor::spec();
    let original_hash = compute_registry_hash(std::slice::from_ref(&spec));

    fn buggy_wgsl() -> String {
        "fn xor(a: u32, b: u32) -> u32 { return 0u; }".to_string()
    }
    spec.wgsl_fn = buggy_wgsl;

    let mutated_hash = compute_registry_hash(std::slice::from_ref(&spec));
    assert_ne!(
        original_hash, mutated_hash,
        "registry_hash must change when the kernel WGSL source changes; \
         otherwise T43 certificate replay is live."
    );
}

#[test]
fn registry_hash_changes_when_law_set_changes() {
    // P1.20-F3: a contributor adding or removing a law from spec.toml
    // (T03/T04/T05) must mutate the cert hash so the cert no longer
    // verifies against the new spec.
    let mut spec = crate::spec::primitive::xor::spec();
    let original_hash = compute_registry_hash(std::slice::from_ref(&spec));

    spec.laws.clear();
    let mutated_hash = compute_registry_hash(std::slice::from_ref(&spec));
    assert_ne!(
        original_hash, mutated_hash,
        "registry_hash must change when the law set changes."
    );
}

/// L.1.26: the registry hash must be invariant under a permutation of
/// the input spec slice. Without canonical sorting, two equivalent
/// registries produced different certificates — a subtle source of
/// certificate drift on any reordering refactor.
#[test]
fn registry_hash_is_permutation_invariant() {
    let a = crate::spec::primitive::xor::spec();
    let b = crate::spec::primitive::and::spec();
    let ab = compute_registry_hash(&[a.clone(), b.clone()]);
    let ba = compute_registry_hash(&[b, a]);
    assert_eq!(
        ab, ba,
        "registry hash must be permutation-invariant; got different digests for [A,B] vs [B,A]"
    );
}

#[test]
fn registry_hash_concatenation_is_unambiguous() {
    // P1.20-F3 follow-up: ("ab", "c") and ("a", "bc") must not collide.
    // blake3-256 provides domain-separated hashing for the registry fingerprint.
    use crate::spec::types::DataType;
    let mut spec_ab = crate::spec::primitive::xor::spec();
    spec_ab.id = "primitive.bitwise.ab";
    spec_ab.signature.inputs = vec![DataType::U32, DataType::U32];

    let mut spec_a_bc = crate::spec::primitive::xor::spec();
    spec_a_bc.id = "primitive.bitwise.a";
    spec_a_bc.signature.inputs = vec![DataType::U32];

    let hash_ab = compute_registry_hash(std::slice::from_ref(&spec_ab));
    let hash_a_bc = compute_registry_hash(std::slice::from_ref(&spec_a_bc));
    assert_ne!(hash_ab, hash_a_bc);
}

#[test]
fn empty_spec_list_is_rejected_with_actionable_message() {
    // P1.20-F12: empty specs used to produce a certificate with spec_version=0
    // via unwrap_or_default(). Reject up front with a Fix: hint instead.
    let err = certify(&CpuMirrorBackend, &[], CertificateStrength::FastCheck).unwrap_err();
    assert!(
        err.starts_with("Fix: certify requires at least one OpSpec"),
        "got: {err}"
    );
}

#[test]
fn certify_rejects_layer4_catalog_defect_by_name() {
    let defect =
        crate::adversarial::mutations::catalog::Mutation::IntegerConstantBitFlip { bit: 0 };
    let err = with_test_mutation_catalog(vec![defect.clone(), defect], || {
        certify(
            &CpuMirrorBackend,
            &[crate::spec::primitive::xor::spec()],
            CertificateStrength::FastCheck,
        )
        .unwrap_err()
    });
    assert!(err.contains("layer4_mutation_gate"), "got: {err}");
    assert!(
        err.contains("integerconstantbitflip") || err.contains("integer_constant_bit_flip"),
        "the exact bit-flip defect id must be named, got: {err}"
    );
}

#[test]
fn parity_failures_are_recorded_in_op_result() {
    let cert = certify(
        &FlippingBackend {
            calls: AtomicUsize::new(0),
        },
        &[crate::spec::primitive::xor::spec()],
        CertificateStrength::FastCheck,
    )
    .unwrap();

    assert_eq!(cert.ops[0].outcome, OpOutcome::Failed);
    assert!(!cert.ops[0].parity_failures.is_empty());
    assert!(cert.ops[0].parity_failures[0]
        .message
        .contains("nondeterministic GPU output"));
}

#[test]
fn certificate_serializes_to_json() {
    let cert = certify(
        &CpuMirrorBackend,
        &[crate::spec::primitive::xor::spec()],
        CertificateStrength::FastCheck,
    )
    .unwrap();
    let json = to_json(&cert).unwrap();
    assert!(json.contains("\"backend_name\": \"cpu-mirror\""));
    assert!(json.contains("EXPLORATORY -- NOT A PROOF"));
    assert!(json.contains("\"integer\": null"));
    assert!(json.contains("\"strength\": \"FastCheck\""));
    assert!(json.contains("\"witness_count\": 10000"));
    assert!(json.contains("\"certificate_hash\""));
}

#[test]
fn human_report_contains_operation_summary() {
    let cert = certify(
        &CpuMirrorBackend,
        &[crate::spec::primitive::xor::spec()],
        CertificateStrength::FastCheck,
    )
    .unwrap();
    let report = to_human(&cert);
    assert!(report.contains("vyre conformance certificate"));
    assert!(report.contains("EXPLORATORY -- NOT A PROOF"));
    assert!(report.contains("verification: FastCheck, 10000 u32 witnesses per law"));
    assert!(report.contains("primitive.bitwise.xor"));
}

#[test]
fn engine_invariant_failures_gate_claimed_levels() {
    let mut spec = crate::spec::primitive::xor::spec();
    spec.laws.clear();

    let cert = certify(&CpuMirrorBackend, &[spec], CertificateStrength::Standard).unwrap();

    assert!(cert.ops[0].parity_passed);
    assert_eq!(cert.ops[0].outcome, OpOutcome::Passed);
    assert!(
        cert.engines
            .iter()
            .any(|engine| engine.status == EngineStatus::Fail),
        "cpu mirror op backend does not implement the engine IR invariant surface"
    );
    assert_eq!(cert.level.integer, None);
    assert_eq!(cert.integer_track.level, Some(ConformanceLevel::L2));
}