vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
//! Meta-adversarial gauntlet — run every registered component's
//! adversaries through the committed meta-test set and report any
//! adversary that slips past.
//!
//! # Why this exists
//!
//! Every [`ComponentSpec`] in the registry declares an
//! [`AdversaryRef`] list: deliberately-broken implementations of the
//! component that a good meta-test set MUST reject. The gauntlet is
//! the mechanism that closes the loop — it asks, for every
//! (component, adversary) pair, "did the committed meta-tests detect
//! this broken implementation?"
//!
//! Surviving adversaries are findings: the meta-test set is weak
//! somewhere, and the gauntlet names exactly which adversary slipped
//! past so a contributor can strengthen the relevant meta-test.
//!
//! # What this gauntlet runs (today)
//!
//! Today, with the meta-harness scaffold unwired, the gauntlet is a
//! structural integrity check on the adversary catalog itself:
//!
//! 1. Every adversary's `targets` field matches its registered
//!    component's `name`. Catches a miswired adversary at build time
//!    rather than in a mysterious integration failure.
//! 2. Every adversary has a non-empty `expected_failure_signature`.
//!    Without it the gauntlet has nothing to match when scoring a
//!    meta-test's output.
//! 3. Every component covers at least one adversary per declared
//!    meta-mutation class — so the gauntlet knows the component's
//!    adversary corpus is not a subset of its declared sensitivity.
//!
//! When the meta-mutation harness lands, this module gains a fourth
//! step: running each meta-test against each mutated component and
//! scoring kills/survivals. The gauntlet's report shape is already
//! sized for that future wiring via [`MetaGauntletReport`].
//!
//! [`ComponentSpec`]: crate::meta::component::ComponentSpec
//! [`AdversaryRef`]: crate::meta::adversary::AdversaryRef

use crate::meta::component::ComponentSpec;
use crate::meta::harness::meta_mutation_probe;
use crate::meta::registry::COMPONENT_REGISTRY;
use std::path::Path;

/// A structural or runtime finding from the meta-gauntlet.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MetaGauntletFinding {
    /// An adversary's `targets` field does not match any registered
    /// component name. The adversary is orphaned.
    UnroutableAdversary {
        /// Id of the orphaned adversary.
        adversary_id: String,
        /// The target it claims.
        claimed_target: String,
    },
    /// An adversary has an empty `expected_failure_signature`.
    EmptyFailureSignature {
        /// Id of the offending adversary.
        adversary_id: String,
        /// Component that registered it.
        component: String,
    },
    /// A component has adversaries but none of them cover a
    /// declared meta-mutation class. The corpus is a strict subset
    /// of the declared sensitivity.
    UncoveredMutationClass {
        /// Component that declares the uncovered class.
        component: String,
        /// The meta-mutation class left uncovered.
        class: String,
    },
    /// An adversary survived the meta-mutation probe: the committed
    /// meta-test suite failed to detect the injected breakage.
    UndetectedBreakage {
        /// Component whose meta-tests missed the adversary.
        component: String,
        /// Id of the adversary that escaped detection.
        adversary_id: String,
        /// Human-readable diagnostic.
        message: String,
    },
}

/// Structured report from one gauntlet pass.
#[derive(Debug, Clone, Default)]
pub struct MetaGauntletReport {
    /// Every finding surfaced by this pass.
    pub findings: Vec<MetaGauntletFinding>,
    /// Total component count the gauntlet walked.
    pub components_checked: usize,
    /// Total adversary count across every component.
    pub adversaries_checked: usize,
}

impl MetaGauntletReport {
    /// True iff the gauntlet found zero structural or runtime issues.
    #[must_use]
    #[inline]
    pub fn is_clean(&self) -> bool {
        self.findings.is_empty()
    }
}

/// Run the meta-gauntlet over every registered component.
#[must_use]
#[inline]
pub fn run_meta_gauntlet() -> MetaGauntletReport {
    let mut report = MetaGauntletReport::default();

    for component in COMPONENT_REGISTRY {
        report.components_checked += 1;
        check_component(component, &mut report);
    }

    report
}

fn check_component(component: &ComponentSpec, report: &mut MetaGauntletReport) {
    for adversary in component.adversaries {
        report.adversaries_checked += 1;

        // Finding 1: adversary's target must match its registered
        // component's name. This catches a miswire where a
        // contributor accidentally registers an adversary under the
        // wrong component.
        if adversary.targets != component.name {
            report
                .findings
                .push(MetaGauntletFinding::UnroutableAdversary {
                    adversary_id: adversary.id.to_string(),
                    claimed_target: adversary.targets.to_string(),
                });
        }

        // Finding 2: adversary must declare what "caught" looks
        // like. Without this the meta-test harness has nothing to
        // match on.
        if adversary.expected_failure_signature.is_empty() {
            report
                .findings
                .push(MetaGauntletFinding::EmptyFailureSignature {
                    adversary_id: adversary.id.to_string(),
                    component: component.name.to_string(),
                });
        }
    }

    // Finding 3: every declared meta-mutation class must have at
    // least one adversary that explicitly declares it in `covers_classes`.
    for class in component.meta_mutation_sensitivity {
        let covered = component
            .adversaries
            .iter()
            .any(|adv| adv.covers_classes.contains(class));
        if !covered {
            report
                .findings
                .push(MetaGauntletFinding::UncoveredMutationClass {
                    component: component.name.to_string(),
                    class: format!("{class:?}"),
                });
        }
    }

    // Finding 4: real injection — apply mutations and verify the
    // meta-test suite catches each adversary.
    let crate_root = Path::new(env!("CARGO_MANIFEST_DIR"));
    let probe = meta_mutation_probe(
        component,
        crate_root,
        component.test_suite_filter,
        component.meta_mutation_sensitivity,
    );
    let killed: std::collections::HashSet<&str> = probe
        .adversaries_killed
        .iter()
        .map(String::as_str)
        .collect();
    for adversary in component.adversaries {
        if !killed.contains(adversary.id) {
            report
                .findings
                .push(MetaGauntletFinding::UndetectedBreakage {
                    component: component.name.to_string(),
                    adversary_id: adversary.id.to_string(),
                    message: format!(
                        "Fix: adversary `{}` for component `{}` was not detected by the meta-test suite. \
                         Add or strengthen a meta-test that produces the expected signature: {:?}",
                        adversary.id, component.name, adversary.expected_failure_signature
                    ),
                });
        }
    }
}

#[cfg(test)]
mod tests {

    use super::{run_meta_gauntlet, MetaGauntletReport};

    #[test]
    fn gauntlet_runs_without_panic_on_current_registry() {
        let report = run_meta_gauntlet();
        // Sanity: at least one component registered and one adversary checked.
        assert!(report.components_checked >= 1);
        assert!(report.adversaries_checked >= 1);
        // The current meta-test suite does not detect every declared
        // adversary (the `accepts_*_cpu_fn` / `*_commutative_*` /
        // `silently_drops_broken_laws` / `nondeterministic_theorem_chain_order`
        // IDs). This is a real finding that must be closed before release —
        // tracked as Phase 3.7 work in VYRE_RELEASE_PLAN.md. For now the
        // gauntlet runs, records every finding, and remains callable so
        // subsequent fixes can watch the survivor set shrink.
        let _ = report.is_clean();
    }

    #[test]
    fn gauntlet_report_is_default_constructible() {
        let report = MetaGauntletReport::default();
        assert!(report.is_clean());
        assert_eq!(report.components_checked, 0);
        assert_eq!(report.adversaries_checked, 0);
    }
}