aprender_contracts_cli/commands/
verify_structure.rs1use std::path::Path;
11
12use super::certify::analyze_config;
13
14pub fn run(
16 contract_dir: &Path,
17 config_json: Option<&Path>,
18 model_file: Option<&Path>,
19) -> Result<(), Box<dyn std::error::Error>> {
20 println!("pv verify-structure — Architecture Structure Verification");
21 println!("=========================================================");
22 println!();
23
24 let arch_path = find_contract(contract_dir, "apr-architecture-schema-v1");
26 let config_path = find_contract(contract_dir, "model-config-algebra-v1");
27 let shape_path = find_contract(contract_dir, "tensor-shape-flow-v1");
28
29 let mut found = 0;
30 let mut missing = Vec::new();
31
32 for (name, path) in [
33 ("apr-architecture-schema-v1", &arch_path),
34 ("model-config-algebra-v1", &config_path),
35 ("tensor-shape-flow-v1", &shape_path),
36 ] {
37 if let Some(p) = path {
38 let contract = provable_contracts::schema::parse_contract(p)?;
39 let eq_count = contract.equations.len();
40 let has_assumes = contract
41 .equations
42 .values()
43 .filter(|e| e.assumes.is_some())
44 .count();
45 let has_guarantees = contract
46 .equations
47 .values()
48 .filter(|e| e.guarantees.is_some())
49 .count();
50 println!(
51 " ✓ {name}: {eq_count} equations, {has_assumes} assumes, {has_guarantees} guarantees"
52 );
53 found += 1;
54 } else {
55 println!(" ✗ {name}: not found");
56 missing.push(name);
57 }
58 }
59 println!();
60
61 if let Some(cfg) = config_json {
63 if cfg.exists() {
64 analyze_config(cfg)?;
65 } else {
66 println!("Config file not found: {}", cfg.display());
67 }
68 } else {
69 println!("No --config provided. Use --config path/to/config.json for structural analysis.");
70 }
71
72 if let Some(mf) = model_file {
74 println!("Model file: {}", mf.display());
75 println!(" ⚠ Model file parsing not yet implemented (P0-4 phase 2)");
76 println!(" Planned: enumerate actual tensors, compare shapes to arch-schema");
77 }
78
79 println!();
80 if missing.is_empty() && found == 3 {
81 println!("Result: PASS — all 3 architecture contracts found with composition data");
82 } else {
83 println!(
84 "Result: PARTIAL — {found}/3 contracts found, {} missing",
85 missing.len()
86 );
87 }
88
89 Ok(())
90}
91
92fn find_contract(dir: &Path, stem: &str) -> Option<std::path::PathBuf> {
93 let direct = dir.join(format!("{stem}.yaml"));
94 if direct.exists() {
95 return Some(direct);
96 }
97 let entries = std::fs::read_dir(dir).ok()?;
100 for entry in entries.flatten() {
101 let path = entry.path();
102 if path.is_dir() {
103 if let Some(found) = find_contract(&path, stem) {
104 return Some(found);
105 }
106 }
107 }
108 None
109}