1use std::fmt::Write;
8
9use crate::binding::BindingRegistry;
10use crate::proof_status::compute_proof_level;
11use crate::schema::{Contract, ObligationType};
12
13#[path = "explain_render.rs"]
14mod explain_render;
15use explain_render::{
16 write_binding_status, write_enforcement, write_falsification_tests, write_kani_harnesses,
17 write_kernel_phases, write_obligations, write_qa_gate, write_simd_dispatch,
18 write_verification_ladder,
19};
20
21#[cfg(test)]
22#[allow(clippy::all)]
23#[path = "explain_tests.rs"]
24mod tests;
25
26pub fn obligation_pattern(ot: ObligationType) -> &'static str {
28 match ot {
29 ObligationType::Invariant => "∀x ∈ Domain: P(f(x)) — property holds for all inputs",
30 ObligationType::Equivalence => "∀x: |f(x) - g(x)| < ε — two implementations agree",
31 ObligationType::Bound => "∀x: a ≤ f(x)_i ≤ b — output range bounded",
32 ObligationType::Monotonicity => "x_i > x_j → f(x)_i > f(x)_j — order preserved",
33 ObligationType::Idempotency => "f(f(x)) = f(x) — applying twice gives same result",
34 ObligationType::Linearity => "f(αx) = α·f(x) — homogeneous scaling",
35 ObligationType::Symmetry => "f(permute(x)) related to f(x) — permutation property",
36 ObligationType::Associativity => "(a ⊕ b) ⊕ c = a ⊕ (b ⊕ c) — grouping invariant",
37 ObligationType::Conservation => "Q(before) = Q(after) — conserved quantity",
38 ObligationType::Ordering => "a ≤ b → f(a) ≤ f(b) — order relation maintained",
39 ObligationType::Completeness => "∀ required elements present — completeness verified",
40 ObligationType::Soundness => "∀x: P(x) → Q(f(x)) — soundness of transformation",
41 ObligationType::Involution => "f(f(x)) = x — involution (self-inverse)",
42 ObligationType::Determinism => "f(x) = f(x) — deterministic output for same input",
43 ObligationType::Roundtrip => "decode(encode(x)) = x — roundtrip fidelity",
44 ObligationType::StateMachine => "S × A → S — valid state transitions",
45 ObligationType::Classification => "f(x) ∈ C — output belongs to valid class set",
46 ObligationType::Independence => "P(A∩B) = P(A)·P(B) — statistical independence",
47 ObligationType::Termination => "algorithm terminates in finite steps",
48 ObligationType::Precondition => "P(input) — caller must guarantee before call",
50 ObligationType::Postcondition => "P(in) → Q(out) — kernel guarantees if pre holds",
51 ObligationType::Frame => "modifies(S) ∧ preserves(T\\S) — only S may change",
52 ObligationType::LoopInvariant => "∀ iter i: P(state_i) — maintained across iterations",
53 ObligationType::LoopVariant => "V(state) ∈ ℕ, strictly decreasing — termination witness",
54 ObligationType::OldState => "Q(old(state), new(state)) — relates pre to post state",
55 ObligationType::Subcontract => "weaken(pre) ∧ strengthen(post) — behavioral subtyping",
56 ObligationType::Safety => "∀x: safe(f(x)) — memory/IO safety obligation",
57 ObligationType::Liveness => "◇ P — eventually P holds (progress obligation)",
58 }
59}
60
61fn strategy_explanation(strategy: &str) -> &str {
63 match strategy {
64 "exhaustive" => "verify for ALL inputs within bound",
65 "stub_float" => {
66 "assume Lean-proved postconditions on transcendentals, verify surrounding code"
67 }
68 "compositional" => "verify sub-kernels separately, compose proofs",
69 "bounded_int" => "integer-only verification within bound",
70 _ => "bounded model check",
71 }
72}
73
74pub fn explain_contract(
79 contract: &Contract,
80 stem: &str,
81 binding: Option<&BindingRegistry>,
82) -> String {
83 let mut out = String::with_capacity(4096);
84
85 write_header(&mut out, contract, stem);
86 write_what(&mut out, contract);
87 write_equations(&mut out, contract);
88 write_obligations(&mut out, contract);
89 write_verification_ladder(&mut out, contract, binding);
90 write_falsification_tests(&mut out, contract);
91 write_kani_harnesses(&mut out, contract);
92 write_kernel_phases(&mut out, contract);
93 write_simd_dispatch(&mut out, contract);
94 write_enforcement(&mut out, contract);
95 write_qa_gate(&mut out, contract);
96 write_type_invariants(&mut out, contract);
97 write_coq_status(&mut out, contract);
98 write_binding_status(&mut out, contract, stem, binding);
99
100 out
101}
102
103fn write_type_invariants(out: &mut String, contract: &Contract) {
104 if contract.type_invariants.is_empty() {
105 return;
106 }
107 let _ = writeln!(out, "Type invariants (Meyer's class invariants)");
108 let _ = writeln!(
109 out,
110 " These predicates must hold for every instance at every stable state."
111 );
112 let _ = writeln!(out);
113 for inv in &contract.type_invariants {
114 let desc = inv
115 .description
116 .as_deref()
117 .map(|d| format!(" — {d}"))
118 .unwrap_or_default();
119 let _ = writeln!(out, " {} [{}]{}", inv.name, inv.type_name, desc);
120 let _ = writeln!(out, " Predicate: {}", inv.predicate);
121 let _ = writeln!(out);
122 }
123}
124
125fn write_coq_status(out: &mut String, contract: &Contract) {
126 let Some(ref spec) = contract.coq_spec else {
127 return;
128 };
129 let _ = writeln!(out, "Coq verification ({})", spec.module);
130 if spec.obligations.is_empty() {
131 let _ = writeln!(out, " No obligation links defined — stubs only");
132 } else {
133 for ob in &spec.obligations {
134 let _ = writeln!(out, " {} → {} [{}]", ob.links_to, ob.coq_lemma, ob.status);
135 }
136 }
137 let _ = writeln!(out);
138}
139
140#[allow(clippy::too_many_lines)]
142pub fn explain_contract_markdown(
143 contract: &Contract,
144 stem: &str,
145 binding: Option<&BindingRegistry>,
146) -> String {
147 let mut out = String::with_capacity(4096);
148 let _ = writeln!(out, "# {stem}\n");
149 let _ = writeln!(
150 out,
151 "**Version:** {} | **Description:** {}\n",
152 contract.metadata.version, contract.metadata.description
153 );
154
155 if !contract.metadata.references.is_empty() {
156 let _ = writeln!(out, "## References\n");
157 for r in &contract.metadata.references {
158 let _ = writeln!(out, "- {r}");
159 }
160 let _ = writeln!(out);
161 }
162
163 if !contract.equations.is_empty() {
164 let _ = writeln!(out, "## Equations\n");
165 for (name, eq) in &contract.equations {
166 let _ = writeln!(out, "### {name}\n");
167 let latex = crate::latex::math_to_latex(&eq.formula);
168 let _ = writeln!(out, "$$\n{latex}\n$$\n");
169 if let Some(ref dom) = eq.domain {
170 let _ = writeln!(out, "**Domain:** ${}$\n", crate::latex::math_to_latex(dom));
171 }
172 if let Some(ref cod) = eq.codomain {
173 let _ = writeln!(
174 out,
175 "**Codomain:** ${}$\n",
176 crate::latex::math_to_latex(cod)
177 );
178 }
179 if !eq.invariants.is_empty() {
180 let _ = writeln!(out, "**Invariants:**\n");
181 for inv in &eq.invariants {
182 let _ = writeln!(out, "- ${}$", crate::latex::math_to_latex(inv));
183 }
184 let _ = writeln!(out);
185 }
186 if !eq.preconditions.is_empty() {
187 let _ = writeln!(out, "**Preconditions:**\n");
188 for pre in &eq.preconditions {
189 let _ = writeln!(out, "- `{pre}`");
190 }
191 let _ = writeln!(out);
192 }
193 if !eq.postconditions.is_empty() {
194 let _ = writeln!(out, "**Postconditions:**\n");
195 for post in &eq.postconditions {
196 let _ = writeln!(out, "- `{post}`");
197 }
198 let _ = writeln!(out);
199 }
200 }
201 }
202
203 if !contract.proof_obligations.is_empty() {
204 let _ = writeln!(out, "## Proof Obligations\n");
205 let _ = writeln!(out, "| # | Type | Property | Formal |");
206 let _ = writeln!(out, "|---|------|----------|--------|");
207 for (i, ob) in contract.proof_obligations.iter().enumerate() {
208 let formal = ob.formal.as_deref().unwrap_or("");
209 let _ = writeln!(
210 out,
211 "| {} | `{}` | {} | {} |",
212 i + 1,
213 ob.obligation_type,
214 ob.property,
215 formal
216 );
217 }
218 let _ = writeln!(out);
219 }
220
221 let level = compute_proof_level(contract, None);
223 let _ = writeln!(out, "## Verification\n");
224 let _ = writeln!(out, "**Proof level:** {level}\n");
225 if let Some(ref vs) = contract.verification_summary {
226 let _ = writeln!(
227 out,
228 "- Lean: {}/{} proved",
229 vs.l4_lean_proved, vs.total_obligations
230 );
231 }
232 let _ = writeln!(out, "- Kani: {} harnesses", contract.kani_harnesses.len());
233 let _ = writeln!(
234 out,
235 "- Tests: {} falsification\n",
236 contract.falsification_tests.len()
237 );
238
239 if let Some(registry) = binding {
241 let contract_file = format!("{stem}.yaml");
242 let has_bindings = contract.equations.keys().any(|eq| {
243 registry
244 .bindings
245 .iter()
246 .any(|b| b.contract == contract_file && b.equation == *eq)
247 });
248 if has_bindings {
249 let _ = writeln!(out, "## Bindings ({})\n", registry.target_crate);
250 let _ = writeln!(out, "| Equation | Status |");
251 let _ = writeln!(out, "|----------|--------|");
252 for eq_name in contract.equations.keys() {
253 let status = registry
254 .bindings
255 .iter()
256 .find(|b| b.contract == contract_file && b.equation == *eq_name)
257 .map_or("missing", |b| match b.status {
258 crate::binding::ImplStatus::Implemented => "implemented",
259 crate::binding::ImplStatus::Partial => "partial",
260 crate::binding::ImplStatus::NotImplemented => "not_implemented",
261 crate::binding::ImplStatus::Pending => "pending",
262 });
263 let _ = writeln!(out, "| {eq_name} | {status} |");
264 }
265 let _ = writeln!(out);
266 }
267 }
268
269 out
270}
271
272#[allow(clippy::disallowed_methods)]
278pub fn explain_contract_json(
279 contract: &Contract,
280 stem: &str,
281 binding: Option<&BindingRegistry>,
282) -> String {
283 let level = compute_proof_level(contract, None);
284
285 let obligations: Vec<serde_json::Value> = contract
286 .proof_obligations
287 .iter()
288 .map(|ob| {
289 let mut obj = serde_json::json!({
290 "type": ob.obligation_type.to_string(),
291 "property": ob.property,
292 "pattern": obligation_pattern(ob.obligation_type),
293 });
294 if let Some(ref f) = ob.formal {
295 obj["formal"] = serde_json::json!(f);
296 }
297 if let Some(t) = ob.tolerance {
298 obj["tolerance"] = serde_json::json!(t);
299 }
300 if let Some(ref lean) = ob.lean {
301 obj["lean"] = serde_json::json!({
302 "theorem": lean.theorem,
303 "status": lean.status.to_string(),
304 });
305 }
306 if let Some(ref req) = ob.requires {
307 obj["requires"] = serde_json::json!(req);
308 }
309 if let Some(ref phase) = ob.applies_to_phase {
310 obj["applies_to_phase"] = serde_json::json!(phase);
311 }
312 if let Some(ref parent) = ob.parent_contract {
313 obj["parent_contract"] = serde_json::json!(parent);
314 }
315 obj
316 })
317 .collect();
318
319 let json = serde_json::json!({
320 "stem": stem,
321 "version": contract.metadata.version,
322 "description": contract.metadata.description,
323 "references": contract.metadata.references,
324 "depends_on": contract.metadata.depends_on,
325 "equations": contract.equations.keys().collect::<Vec<_>>(),
326 "proof_level": level.to_string(),
327 "obligations": obligations,
328 "falsification_tests": contract.falsification_tests.len(),
329 "kani_harnesses": contract.kani_harnesses.len(),
330 "binding": binding.map(|b| {
331 let contract_file = format!("{stem}.yaml");
332 let statuses: std::collections::BTreeMap<String, String> = contract
333 .equations
334 .keys()
335 .map(|eq| {
336 let status = b.bindings.iter()
337 .find(|bi| bi.contract == contract_file && bi.equation == *eq)
338 .map_or_else(|| "missing".to_string(), |bi| bi.status.to_string());
339 (eq.clone(), status)
340 })
341 .collect();
342 serde_json::json!({
343 "target_crate": b.target_crate,
344 "equations": statuses,
345 })
346 }),
347 });
348
349 serde_json::to_string_pretty(&json).unwrap_or_else(|_| "{}".to_string())
350}
351
352fn write_header(out: &mut String, contract: &Contract, stem: &str) {
353 use crate::schema::ContractKind;
354 let kind_tag = if contract.kind() == ContractKind::Kernel {
355 String::new()
356 } else {
357 format!(" [{}]", contract.kind())
358 };
359 let _ = writeln!(out, "{stem} (v{}){kind_tag}", contract.metadata.version);
360 let _ = writeln!(out, "{}", contract.metadata.description);
361 let _ = writeln!(out);
362}
363
364fn write_what(out: &mut String, contract: &Contract) {
365 let _ = writeln!(out, "What this contract specifies");
366
367 let refs = &contract.metadata.references;
368 if refs.is_empty() {
369 let _ = writeln!(
370 out,
371 " This contract specifies {}.",
372 contract.metadata.description
373 );
374 } else {
375 let _ = write!(
376 out,
377 " This contract specifies {}. It derives from",
378 contract.metadata.description
379 );
380 for (i, r) in refs.iter().enumerate() {
381 if i == 0 {
382 let _ = write!(out, " {r}");
383 } else {
384 let _ = write!(out, " and {r}");
385 }
386 }
387 let _ = writeln!(out, ".");
388 }
389
390 if !contract.metadata.depends_on.is_empty() {
391 let _ = write!(out, " Depends on:");
392 for dep in &contract.metadata.depends_on {
393 let _ = write!(out, " {dep}");
394 }
395 let _ = writeln!(out);
396 }
397
398 let _ = writeln!(out);
399}
400
401fn write_equations(out: &mut String, contract: &Contract) {
402 if contract.equations.is_empty() {
403 return;
404 }
405 let _ = writeln!(out, "Governing equations");
406 let _ = writeln!(out);
407
408 for (name, eq) in &contract.equations {
409 let _ = writeln!(out, " {name}");
410 let _ = writeln!(out, " {}", eq.formula);
411
412 if let Some(ref dom) = eq.domain {
413 let _ = writeln!(out, " Domain: {dom}");
414 }
415 if let Some(ref cod) = eq.codomain {
416 let _ = writeln!(out, " Range: {cod}");
417 }
418
419 if !eq.invariants.is_empty() {
420 let _ = writeln!(out);
421 let _ = writeln!(out, " Invariants:");
422 for (i, inv) in eq.invariants.iter().enumerate() {
423 let _ = writeln!(out, " {}. {inv}", i + 1);
424 }
425 }
426
427 if !eq.preconditions.is_empty() {
428 let _ = writeln!(out);
429 let _ = writeln!(out, " Preconditions (caller must guarantee):");
430 for pre in &eq.preconditions {
431 let _ = writeln!(out, " - {pre}");
432 }
433 }
434
435 if !eq.postconditions.is_empty() {
436 let _ = writeln!(out);
437 let _ = writeln!(out, " Postconditions (kernel guarantees):");
438 for post in &eq.postconditions {
439 let _ = writeln!(out, " - {post}");
440 }
441 }
442
443 if let Some(ref theorem) = eq.lean_theorem {
444 let _ = writeln!(out, " Lean theorem: {theorem}");
445 }
446
447 let _ = writeln!(out);
448 }
449}