324 lines
11 KiB
Rust
324 lines
11 KiB
Rust
use std::fs;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
struct ProofGate {
|
|
name: String,
|
|
critical: bool,
|
|
check: Box<dyn Fn() -> GateStatus>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum GateStatus {
|
|
Pass { evidence: String },
|
|
Fail { reason: String },
|
|
Skipped { reason: String },
|
|
}
|
|
|
|
fn check_gate_1_contracts() -> GateStatus {
|
|
// Check if golden test fixtures exist and are valid JSON
|
|
let manifest_dir = Path::new("manifests");
|
|
if !manifest_dir.exists() {
|
|
return GateStatus::Fail {
|
|
reason: "manifests/ directory does not exist".to_string(),
|
|
};
|
|
}
|
|
|
|
let golden_files = [
|
|
"2026-05-26-domedog-deepseek-cache-result.json",
|
|
"2026-05-26-osa-deepseek-cache-result.json",
|
|
"2026-05-26-domedog-runtime-inventory.json",
|
|
"2026-05-26-osa-runtime-inventory.json",
|
|
"2026-05-26-debby-runtime-inventory.json",
|
|
"2026-05-26-osa-watchdog-host-status.json",
|
|
];
|
|
|
|
let mut valid_count = 0;
|
|
for file in &golden_files {
|
|
let path = manifest_dir.join(file);
|
|
if path.exists() {
|
|
match fs::read_to_string(&path) {
|
|
Ok(content) => {
|
|
if serde_json::from_str::<serde_json::Value>(&content).is_ok() {
|
|
valid_count += 1;
|
|
}
|
|
}
|
|
Err(_) => continue,
|
|
}
|
|
}
|
|
}
|
|
|
|
if valid_count >= 5 {
|
|
GateStatus::Pass {
|
|
evidence: format!(
|
|
"{}/{} golden fixtures valid",
|
|
valid_count,
|
|
golden_files.len()
|
|
),
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: format!(
|
|
"Only {}/{} golden fixtures valid",
|
|
valid_count,
|
|
golden_files.len()
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_gate_2_cache_manifest() -> GateStatus {
|
|
let manifest_dir = Path::new("manifests");
|
|
let osa_manifest = manifest_dir.join("2026-05-26-osa-deepseek-cache-result.json");
|
|
let domedog_manifest = manifest_dir.join("2026-05-26-domedog-deepseek-cache-result.json");
|
|
|
|
let mut evidence = Vec::new();
|
|
|
|
if osa_manifest.exists() {
|
|
if let Ok(content) = fs::read_to_string(&osa_manifest) {
|
|
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
|
|
let cache_hit = json["cache_hit_observed"].as_bool().unwrap_or(false);
|
|
let hit_tokens = json["cache_hit_tokens"].as_u64().unwrap_or(0);
|
|
if cache_hit && hit_tokens > 0 {
|
|
evidence.push(format!("osa: {} cache hit tokens", hit_tokens));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if domedog_manifest.exists() {
|
|
if let Ok(content) = fs::read_to_string(&domedog_manifest) {
|
|
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
|
|
let cache_hit = json["cache_hit_observed"].as_bool().unwrap_or(false);
|
|
let hit_tokens = json["cache_hit_tokens"].as_u64().unwrap_or(0);
|
|
if cache_hit && hit_tokens > 0 {
|
|
evidence.push(format!("domedog: {} cache hit tokens", hit_tokens));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if evidence.len() >= 2 {
|
|
GateStatus::Pass {
|
|
evidence: evidence.join("; "),
|
|
}
|
|
} else if evidence.len() == 1 {
|
|
GateStatus::Pass {
|
|
evidence: format!("Partial: {}", evidence[0]),
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: "No cache hit manifests found".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_gate_3_runtime_inventory() -> GateStatus {
|
|
let manifest_dir = Path::new("manifests");
|
|
let platforms = [("osa", "FreeBSD"), ("domedog", "Linux"), ("debby", "Linux")];
|
|
let mut evidence = Vec::new();
|
|
|
|
for (host, expected_os) in &platforms {
|
|
let manifest_path =
|
|
manifest_dir.join(format!("2026-05-26-{}-runtime-inventory.json", host));
|
|
if manifest_path.exists() {
|
|
if let Ok(content) = fs::read_to_string(&manifest_path) {
|
|
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
|
|
let os = json["os"].as_str().unwrap_or("");
|
|
let pi = json["pi"].as_str().unwrap_or("none");
|
|
if os.contains(expected_os) {
|
|
evidence.push(format!("{}: {} (pi: {})", host, expected_os, pi));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if evidence.len() >= 2 {
|
|
GateStatus::Pass {
|
|
evidence: evidence.join("; "),
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: format!(
|
|
"Only {}/{} platforms have valid runtime inventories",
|
|
evidence.len(),
|
|
platforms.len()
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_gate_4_cross_platform() -> GateStatus {
|
|
// Check if build passes (Linux)
|
|
let result = Command::new("cargo")
|
|
.args(["check", "--workspace"])
|
|
.output();
|
|
|
|
match result {
|
|
Ok(output) if output.status.success() => GateStatus::Pass {
|
|
evidence: "cargo check --workspace passed".to_string(),
|
|
},
|
|
Ok(output) => {
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
GateStatus::Fail {
|
|
reason: format!(
|
|
"Build failed: {}",
|
|
stderr.lines().take(3).collect::<Vec<_>>().join(" ")
|
|
),
|
|
}
|
|
}
|
|
Err(e) => GateStatus::Fail {
|
|
reason: format!("Failed to run cargo check: {}", e),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn check_gate_5_watchdog() -> GateStatus {
|
|
// Check if watchdog host status manifest exists
|
|
let manifest_path = Path::new("manifests/2026-05-26-osa-watchdog-host-status.json");
|
|
if manifest_path.exists() {
|
|
match fs::read_to_string(manifest_path) {
|
|
Ok(content) => {
|
|
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
|
|
if json["source"].as_str() == Some("watchdog-socket") {
|
|
let mode = json["mode"].as_str().unwrap_or("unknown");
|
|
GateStatus::Pass {
|
|
evidence: format!(
|
|
"osa watchdog socket read successful (mode: {})",
|
|
mode
|
|
),
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: "Manifest exists but source is not watchdog-socket".to_string(),
|
|
}
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: "Manifest is not valid JSON".to_string(),
|
|
}
|
|
}
|
|
}
|
|
Err(e) => GateStatus::Fail {
|
|
reason: format!("Failed to read manifest: {}", e),
|
|
},
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: "osa watchdog host status manifest not found".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_gate_6_caller_inventory() -> GateStatus {
|
|
// Gate #6 is gated on precondition: CONTROLPLANE_RUNNER=pi in production
|
|
// Check if caller inventory exists
|
|
let inventory_path = Path::new("docs/CALLER-INVENTORY.md");
|
|
if inventory_path.exists() {
|
|
match fs::read_to_string(inventory_path) {
|
|
Ok(content) => {
|
|
if content.contains("agent-runner.ts") && content.contains("KEEP") {
|
|
GateStatus::Skipped {
|
|
reason: "Caller inventory documented, awaiting production verification of CONTROLPLANE_RUNNER=pi".to_string()
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: "Caller inventory exists but does not document agent-runner.ts"
|
|
.to_string(),
|
|
}
|
|
}
|
|
}
|
|
Err(e) => GateStatus::Fail {
|
|
reason: format!("Failed to read caller inventory: {}", e),
|
|
},
|
|
}
|
|
} else {
|
|
GateStatus::Fail {
|
|
reason: "docs/CALLER-INVENTORY.md not found".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
println!("🔍 Colibri Proof Gate Tracker");
|
|
println!("═════════════════════════════\n");
|
|
|
|
let gates: Vec<ProofGate> = vec![
|
|
ProofGate {
|
|
name: "gate-1-contracts".to_string(),
|
|
critical: true,
|
|
check: Box::new(check_gate_1_contracts),
|
|
},
|
|
ProofGate {
|
|
name: "gate-2-cache-manifest".to_string(),
|
|
critical: true,
|
|
check: Box::new(check_gate_2_cache_manifest),
|
|
},
|
|
ProofGate {
|
|
name: "gate-3-runtime-inventory".to_string(),
|
|
critical: true,
|
|
check: Box::new(check_gate_3_runtime_inventory),
|
|
},
|
|
ProofGate {
|
|
name: "gate-4-cross-platform".to_string(),
|
|
critical: true,
|
|
check: Box::new(check_gate_4_cross_platform),
|
|
},
|
|
ProofGate {
|
|
name: "gate-5-watchdog".to_string(),
|
|
critical: true,
|
|
check: Box::new(check_gate_5_watchdog),
|
|
},
|
|
ProofGate {
|
|
name: "gate-6-caller-inventory".to_string(),
|
|
critical: false, // Precondition check
|
|
check: Box::new(check_gate_6_caller_inventory),
|
|
},
|
|
];
|
|
|
|
let mut critical_passed = 0;
|
|
let mut critical_total = 0;
|
|
let mut all_passed = 0;
|
|
|
|
for gate in &gates {
|
|
if gate.critical {
|
|
critical_total += 1;
|
|
}
|
|
|
|
let status = (gate.check)();
|
|
let critical_marker = if gate.critical { " 🔒" } else { " ⏸️" };
|
|
|
|
match status {
|
|
GateStatus::Pass { evidence } => {
|
|
println!("✅ {}{}: {}", gate.name, critical_marker, evidence);
|
|
all_passed += 1;
|
|
if gate.critical {
|
|
critical_passed += 1;
|
|
}
|
|
}
|
|
GateStatus::Fail { reason } => {
|
|
println!("❌ {}{}: {}", gate.name, critical_marker, reason);
|
|
}
|
|
GateStatus::Skipped { reason } => {
|
|
println!("⏭️ {}{}: {}", gate.name, critical_marker, reason);
|
|
all_passed += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
println!("\n═════════════════════════════");
|
|
println!("Summary: {}/{} gates passing", all_passed, gates.len());
|
|
println!(
|
|
"Critical: {}/{} gates passing",
|
|
critical_passed, critical_total
|
|
);
|
|
|
|
if critical_passed < critical_total {
|
|
println!("\n⚠️ Some critical gates are failing!");
|
|
std::process::exit(1);
|
|
} else {
|
|
println!("\n✅ All critical gates are passing!");
|
|
std::process::exit(0);
|
|
}
|
|
}
|