use std::collections::BTreeSet;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::time::Instant;
use clap::Parser;
use crate::generate::archetypes::ALL_ARCHETYPES;
use crate::generate::emit::{generate, GenError, GenerationPlan, TemplateKind};
use crate::op_registry::all_specs;
use crate::verify::harnesses::mutation::{AppliedMutation, MutationApplyError, MutationOutcome};
use crate::OpSpec;
pub mod core {
use super::*;
#[derive(Debug, Clone)]
pub struct CalibrationReport {
pub op_id: String,
pub generated_count: usize,
pub unique_oracles: Vec<String>,
pub generation_duration_ms: u128,
pub test_duration_ms: u128,
pub mutation_duration_ms: u128,
pub generated_killed: usize,
pub generated_survived: usize,
pub generated_skipped: usize,
pub handwritten_killed: Option<usize>,
pub handwritten_survived: Option<usize>,
pub handwritten_skipped: Option<usize>,
pub findings: Vec<String>,
pub pending_upstream: Option<String>,
pub test_panicked: bool,
}
impl CalibrationReport {
#[inline]
pub fn format_report(&self) -> String {
let mut lines = Vec::new();
lines.push(format!(
"=== Phase 6 Calibration Report: {} ===",
self.op_id
));
lines.push(format!("generated_files: {}", self.generated_count));
lines.push(format!("unique_oracles: {:?}", self.unique_oracles));
lines.push(format!(
"generation_duration_ms: {}",
self.generation_duration_ms
));
lines.push(format!("test_duration_ms: {}", self.test_duration_ms));
lines.push(format!(
"mutation_duration_ms: {}",
self.mutation_duration_ms
));
lines.push(format!("generated_killed: {}", self.generated_killed));
lines.push(format!("generated_survived: {}", self.generated_survived));
lines.push(format!("generated_skipped: {}", self.generated_skipped));
if let Some(k) = self.handwritten_killed {
lines.push(format!("handwritten_killed: {}", k));
}
if let Some(s) = self.handwritten_survived {
lines.push(format!("handwritten_survived: {}", s));
}
if let Some(s) = self.handwritten_skipped {
lines.push(format!("handwritten_skipped: {}", s));
}
if let Some(ref p) = self.pending_upstream {
lines.push(format!("pending_upstream: {}", p));
}
lines.push("findings:".to_string());
for finding in &self.findings {
lines.push(format!(" - {}", finding));
}
lines.push(format!("test_panicked: {}", self.test_panicked));
lines.join("\n")
}
}
#[inline]
pub fn run(op_id: &str) -> CalibrationReport {
let start = Instant::now();
let mut report = CalibrationReport {
op_id: op_id.to_string(),
generated_count: 0,
unique_oracles: Vec::new(),
generation_duration_ms: 0,
test_duration_ms: 0,
mutation_duration_ms: 0,
generated_killed: 0,
generated_survived: 0,
generated_skipped: 0,
handwritten_killed: None,
handwritten_survived: None,
handwritten_skipped: None,
findings: Vec::new(),
pending_upstream: None,
test_panicked: false,
};
let specs = all_specs();
let Some(op) = specs.into_iter().find(|s| s.id == op_id) else {
report.pending_upstream = Some(format!("registry missing op {}", op_id));
return report;
};
let op: &'static OpSpec = Box::leak(Box::new(op));
let gen_start = Instant::now();
let out_dir = std::env::temp_dir().join(format!(
"vyre_phase6_{}_{}",
op_id.replace('.', "_"),
std::process::id()
));
let _ = fs::remove_dir_all(&out_dir);
let plan = GenerationPlan {
ops: vec![op],
archetypes: ALL_ARCHETYPES.to_vec(),
templates: vec![
TemplateKind::OpCorrectness,
TemplateKind::Law,
TemplateKind::BackendEquiv,
TemplateKind::Archetype,
TemplateKind::Validation,
TemplateKind::MutationKill,
],
seed: 0x5151_5151_0000_0001,
};
let gen_report = match generate(&plan, &out_dir) {
Ok(r) => r,
Err(GenError::InvalidPlan { ref reason })
if reason.contains("Phase 5") || reason.contains("target") =>
{
report.pending_upstream = Some("generator".to_string());
return report;
}
Err(GenError::Template(ref e))
if e.to_string().contains("not found") || e.to_string().contains("stub") =>
{
report.pending_upstream = Some("generator/templates".to_string());
return report;
}
Err(e) => {
report.findings.push(format!("generation failed: {}", e));
return report;
}
};
report.generated_count = gen_report.files.len();
let oracles: BTreeSet<_> = gen_report.files.iter().map(|f| f.oracle.clone()).collect();
report.unique_oracles = oracles.into_iter().collect();
report.generation_duration_ms = gen_start.elapsed().as_millis();
if gen_report.files.is_empty() {
report
.findings
.push("generation produced zero files".to_string());
return report;
}
let manifest_dir = env::var_os("CARGO_MANIFEST_DIR")
.map(PathBuf::from)
.unwrap_or_else(|| env::current_dir().expect("Fix: current dir must be available"));
let tests_dir = manifest_dir.join("tests");
let mut combined = String::new();
for file in &gen_report.files {
let src = match fs::read_to_string(&file.path) {
Ok(s) => s,
Err(e) => {
report.findings.push(format!(
"failed to read generated file {}: {}",
file.path.display(),
e
));
continue;
}
};
combined.push_str(&src);
combined.push('\n');
}
let test_file_stem = format!(
"phase6_generated_{}_{}",
op_id.replace('.', "_"),
start.elapsed().as_millis()
);
let test_file_path = tests_dir.join(format!("{}.rs", test_file_stem));
if let Err(e) = fs::write(&test_file_path, &combined) {
report.findings.push(format!(
"failed to write temp test file {}: {}",
test_file_path.display(),
e
));
return report;
}
struct TempTestFile(PathBuf);
impl Drop for TempTestFile {
fn drop(&mut self) {
let _ = fs::remove_file(&self.0);
}
}
let _test_guard = TempTestFile(test_file_path.clone());
let test_start = Instant::now();
let test_result = Command::new("cargo")
.args(["test", "--offline", "--test", &test_file_stem])
.output();
report.test_duration_ms = test_start.elapsed().as_millis();
match test_result {
Ok(output) => {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let combined_out = format!("{}{}", stdout, stderr);
if combined_out.contains("panicked at")
|| combined_out.contains("thread 'main' panicked")
{
report.test_panicked = true;
report
.findings
.push("generated tests caused panics".to_string());
} else if !output.status.success() {
report
.findings
.push("generated tests had structured failures".to_string());
}
}
Err(e) => {
report
.findings
.push(format!("cargo test spawn failed: {}", e));
}
}
let mutation_start = Instant::now();
let catalog_mutations: Vec<CatalogMutation> =
crate::adversarial::mutations::catalog::MUTATION_CATALOG
.iter()
.filter(|m| {
op.mutation_sensitivity
.contains(&crate::adversarial::mutations::catalog::class_of(m))
})
.map(|m| CatalogMutation(m.clone()))
.collect();
let mutation_refs: Vec<&dyn AppliedMutation> = catalog_mutations
.iter()
.map(|m| m as &dyn AppliedMutation)
.collect();
let classes: Vec<_> = op.mutation_sensitivity.iter().copied().collect();
let source_file = manifest_dir.join("src/specs/primitive/add.rs");
if !source_file.exists() {
report
.findings
.push("mutation source src/specs/primitive/add.rs not found".to_string());
} else if mutation_refs.is_empty() {
report
.findings
.push("no mutations selected for this op".to_string());
} else {
let sample = gen_report.files.first();
if let Some(file) = sample {
let gate_report = crate::verify::harnesses::mutation::mutation_probe(
&source_file,
&file.test_name,
&mutation_refs,
&classes,
&[],
);
for result in &gate_report.results {
match result.outcome {
MutationOutcome::Killed => {
report.generated_killed += 1;
}
MutationOutcome::Survived => {
report.generated_survived += 1;
report.findings.push(format!(
"SURVIVOR: {} survived {} ({})",
file.test_name, result.mutation_id, result.description
));
}
MutationOutcome::Skipped { .. } => {
report.generated_skipped += 1;
}
_ => {
report.generated_skipped += 1;
}
}
}
}
}
report.mutation_duration_ms = mutation_start.elapsed().as_millis();
let vyre_tests = manifest_dir.join("../vyre/tests/primitive_programs.rs");
let vyre_source = manifest_dir.join("../vyre/src/ops/primitive/add.rs");
if vyre_tests.exists() && vyre_source.exists() {
let src = match fs::read_to_string(&vyre_tests) {
Ok(s) => s,
Err(e) => {
report
.findings
.push(format!("handwritten comparison skipped: {}", e));
return report;
}
};
let mut test_names = Vec::new();
let lines: Vec<_> = src.lines().collect();
for i in 0..lines.len().saturating_sub(1) {
if lines[i].trim().starts_with("#[test]") {
let next = lines[i + 1].trim();
if let Some(rest) = next.strip_prefix("fn ") {
if let Some(name) = rest.split('(').next() {
test_names.push(name.to_string());
}
}
}
}
let mut hk = 0;
let mut hs = 0;
let mut hskip = 0;
let original_dir = match env::current_dir() {
Ok(dir) => dir,
Err(error) => {
report.findings.push(format!(
"Fix: handwritten comparison skipped because current dir was unavailable: {error}"
));
return report;
}
};
if let Ok(vyre_dir) = manifest_dir.join("../vyre").canonicalize() {
for name in test_names.iter().filter(|n| {
n.contains("add")
|| *n
== "binary_primitive_programs_have_buffer_entry_and_operator_contracts"
}) {
if env::set_current_dir(&vyre_dir).is_err() {
continue;
}
let abs_source = match vyre_source.canonicalize() {
Ok(p) => p,
Err(_) => continue,
};
let gate_report = crate::verify::harnesses::mutation::mutation_probe(
&abs_source,
name,
&mutation_refs,
&classes,
&[],
);
for result in &gate_report.results {
match result.outcome {
MutationOutcome::Killed => hk += 1,
MutationOutcome::Survived => hs += 1,
MutationOutcome::Skipped { .. } => hskip += 1,
_ => hskip += 1,
}
}
}
let _ = env::set_current_dir(&original_dir);
}
report.handwritten_killed = Some(hk);
report.handwritten_survived = Some(hs);
report.handwritten_skipped = Some(hskip);
}
report
}
struct CatalogMutation(crate::adversarial::mutations::catalog::Mutation);
impl AppliedMutation for CatalogMutation {
fn id(&self) -> &str {
leak_string(format!("{:?}", self.0))
}
fn description(&self) -> &str {
leak_string(format!("{:?}", self.0))
}
fn class(&self) -> crate::spec::MutationClass {
crate::adversarial::mutations::catalog::class_of(&self.0)
}
fn apply(&self, source: &str) -> Result<String, MutationApplyError> {
crate::adversarial::mutations::catalog::apply(source, &self.0).map_err(|e| {
MutationApplyError::NotApplicable {
reason: e.to_string(),
}
})
}
}
fn leak_string(s: String) -> &'static str {
Box::leak(s.into_boxed_str())
}
}
#[derive(Debug, Parser)]
pub struct Args {
#[arg(long, default_value = "primitive.math.add")]
pub op: String,
}
pub fn run(args: Args) -> Result<(), Box<dyn std::error::Error>> {
let report = core::run(&args.op);
println!("{}", report.format_report());
Ok(())
}