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
272pub fn explain_contract_json(
274 contract: &Contract,
275 stem: &str,
276 binding: Option<&BindingRegistry>,
277) -> String {
278 let level = compute_proof_level(contract, None);
279
280 let obligations: Vec<serde_json::Value> = contract
281 .proof_obligations
282 .iter()
283 .map(|ob| {
284 let mut obj = serde_json::json!({
285 "type": ob.obligation_type.to_string(),
286 "property": ob.property,
287 "pattern": obligation_pattern(ob.obligation_type),
288 });
289 if let Some(ref f) = ob.formal {
290 obj["formal"] = serde_json::json!(f);
291 }
292 if let Some(t) = ob.tolerance {
293 obj["tolerance"] = serde_json::json!(t);
294 }
295 if let Some(ref lean) = ob.lean {
296 obj["lean"] = serde_json::json!({
297 "theorem": lean.theorem,
298 "status": lean.status.to_string(),
299 });
300 }
301 if let Some(ref req) = ob.requires {
302 obj["requires"] = serde_json::json!(req);
303 }
304 if let Some(ref phase) = ob.applies_to_phase {
305 obj["applies_to_phase"] = serde_json::json!(phase);
306 }
307 if let Some(ref parent) = ob.parent_contract {
308 obj["parent_contract"] = serde_json::json!(parent);
309 }
310 obj
311 })
312 .collect();
313
314 let json = serde_json::json!({
315 "stem": stem,
316 "version": contract.metadata.version,
317 "description": contract.metadata.description,
318 "references": contract.metadata.references,
319 "depends_on": contract.metadata.depends_on,
320 "equations": contract.equations.keys().collect::<Vec<_>>(),
321 "proof_level": level.to_string(),
322 "obligations": obligations,
323 "falsification_tests": contract.falsification_tests.len(),
324 "kani_harnesses": contract.kani_harnesses.len(),
325 "binding": binding.map(|b| {
326 let contract_file = format!("{stem}.yaml");
327 let statuses: std::collections::BTreeMap<String, String> = contract
328 .equations
329 .keys()
330 .map(|eq| {
331 let status = b.bindings.iter()
332 .find(|bi| bi.contract == contract_file && bi.equation == *eq)
333 .map_or_else(|| "missing".to_string(), |bi| bi.status.to_string());
334 (eq.clone(), status)
335 })
336 .collect();
337 serde_json::json!({
338 "target_crate": b.target_crate,
339 "equations": statuses,
340 })
341 }),
342 });
343
344 serde_json::to_string_pretty(&json).unwrap_or_else(|_| "{}".to_string())
345}
346
347fn write_header(out: &mut String, contract: &Contract, stem: &str) {
348 use crate::schema::ContractKind;
349 let kind_tag = if contract.kind() == ContractKind::Kernel {
350 String::new()
351 } else {
352 format!(" [{}]", contract.kind())
353 };
354 let _ = writeln!(out, "{stem} (v{}){kind_tag}", contract.metadata.version);
355 let _ = writeln!(out, "{}", contract.metadata.description);
356 let _ = writeln!(out);
357}
358
359fn write_what(out: &mut String, contract: &Contract) {
360 let _ = writeln!(out, "What this contract specifies");
361
362 let refs = &contract.metadata.references;
363 if refs.is_empty() {
364 let _ = writeln!(
365 out,
366 " This contract specifies {}.",
367 contract.metadata.description
368 );
369 } else {
370 let _ = write!(
371 out,
372 " This contract specifies {}. It derives from",
373 contract.metadata.description
374 );
375 for (i, r) in refs.iter().enumerate() {
376 if i == 0 {
377 let _ = write!(out, " {r}");
378 } else {
379 let _ = write!(out, " and {r}");
380 }
381 }
382 let _ = writeln!(out, ".");
383 }
384
385 if !contract.metadata.depends_on.is_empty() {
386 let _ = write!(out, " Depends on:");
387 for dep in &contract.metadata.depends_on {
388 let _ = write!(out, " {dep}");
389 }
390 let _ = writeln!(out);
391 }
392
393 let _ = writeln!(out);
394}
395
396fn write_equations(out: &mut String, contract: &Contract) {
397 if contract.equations.is_empty() {
398 return;
399 }
400 let _ = writeln!(out, "Governing equations");
401 let _ = writeln!(out);
402
403 for (name, eq) in &contract.equations {
404 let _ = writeln!(out, " {name}");
405 let _ = writeln!(out, " {}", eq.formula);
406
407 if let Some(ref dom) = eq.domain {
408 let _ = writeln!(out, " Domain: {dom}");
409 }
410 if let Some(ref cod) = eq.codomain {
411 let _ = writeln!(out, " Range: {cod}");
412 }
413
414 if !eq.invariants.is_empty() {
415 let _ = writeln!(out);
416 let _ = writeln!(out, " Invariants:");
417 for (i, inv) in eq.invariants.iter().enumerate() {
418 let _ = writeln!(out, " {}. {inv}", i + 1);
419 }
420 }
421
422 if !eq.preconditions.is_empty() {
423 let _ = writeln!(out);
424 let _ = writeln!(out, " Preconditions (caller must guarantee):");
425 for pre in &eq.preconditions {
426 let _ = writeln!(out, " - {pre}");
427 }
428 }
429
430 if !eq.postconditions.is_empty() {
431 let _ = writeln!(out);
432 let _ = writeln!(out, " Postconditions (kernel guarantees):");
433 for post in &eq.postconditions {
434 let _ = writeln!(out, " - {post}");
435 }
436 }
437
438 if let Some(ref theorem) = eq.lean_theorem {
439 let _ = writeln!(out, " Lean theorem: {theorem}");
440 }
441
442 let _ = writeln!(out);
443 }
444}