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 }
57}
58
59fn strategy_explanation(strategy: &str) -> &str {
61 match strategy {
62 "exhaustive" => "verify for ALL inputs within bound",
63 "stub_float" => {
64 "assume Lean-proved postconditions on transcendentals, verify surrounding code"
65 }
66 "compositional" => "verify sub-kernels separately, compose proofs",
67 "bounded_int" => "integer-only verification within bound",
68 _ => "bounded model check",
69 }
70}
71
72pub fn explain_contract(
77 contract: &Contract,
78 stem: &str,
79 binding: Option<&BindingRegistry>,
80) -> String {
81 let mut out = String::with_capacity(4096);
82
83 write_header(&mut out, contract, stem);
84 write_what(&mut out, contract);
85 write_equations(&mut out, contract);
86 write_obligations(&mut out, contract);
87 write_verification_ladder(&mut out, contract, binding);
88 write_falsification_tests(&mut out, contract);
89 write_kani_harnesses(&mut out, contract);
90 write_kernel_phases(&mut out, contract);
91 write_simd_dispatch(&mut out, contract);
92 write_enforcement(&mut out, contract);
93 write_qa_gate(&mut out, contract);
94 write_type_invariants(&mut out, contract);
95 write_coq_status(&mut out, contract);
96 write_binding_status(&mut out, contract, stem, binding);
97
98 out
99}
100
101fn write_type_invariants(out: &mut String, contract: &Contract) {
102 if contract.type_invariants.is_empty() {
103 return;
104 }
105 let _ = writeln!(out, "Type invariants (Meyer's class invariants)");
106 let _ = writeln!(
107 out,
108 " These predicates must hold for every instance at every stable state."
109 );
110 let _ = writeln!(out);
111 for inv in &contract.type_invariants {
112 let desc = inv
113 .description
114 .as_deref()
115 .map(|d| format!(" — {d}"))
116 .unwrap_or_default();
117 let _ = writeln!(out, " {} [{}]{}", inv.name, inv.type_name, desc);
118 let _ = writeln!(out, " Predicate: {}", inv.predicate);
119 let _ = writeln!(out);
120 }
121}
122
123fn write_coq_status(out: &mut String, contract: &Contract) {
124 let Some(ref spec) = contract.coq_spec else {
125 return;
126 };
127 let _ = writeln!(out, "Coq verification ({})", spec.module);
128 if spec.obligations.is_empty() {
129 let _ = writeln!(out, " No obligation links defined — stubs only");
130 } else {
131 for ob in &spec.obligations {
132 let _ = writeln!(out, " {} → {} [{}]", ob.links_to, ob.coq_lemma, ob.status);
133 }
134 }
135 let _ = writeln!(out);
136}
137
138#[allow(clippy::too_many_lines)]
140pub fn explain_contract_markdown(
141 contract: &Contract,
142 stem: &str,
143 binding: Option<&BindingRegistry>,
144) -> String {
145 let mut out = String::with_capacity(4096);
146 let _ = writeln!(out, "# {stem}\n");
147 let _ = writeln!(
148 out,
149 "**Version:** {} | **Description:** {}\n",
150 contract.metadata.version, contract.metadata.description
151 );
152
153 if !contract.metadata.references.is_empty() {
154 let _ = writeln!(out, "## References\n");
155 for r in &contract.metadata.references {
156 let _ = writeln!(out, "- {r}");
157 }
158 let _ = writeln!(out);
159 }
160
161 if !contract.equations.is_empty() {
162 let _ = writeln!(out, "## Equations\n");
163 for (name, eq) in &contract.equations {
164 let _ = writeln!(out, "### {name}\n");
165 let latex = crate::latex::math_to_latex(&eq.formula);
166 let _ = writeln!(out, "$$\n{latex}\n$$\n");
167 if let Some(ref dom) = eq.domain {
168 let _ = writeln!(out, "**Domain:** ${}$\n", crate::latex::math_to_latex(dom));
169 }
170 if let Some(ref cod) = eq.codomain {
171 let _ = writeln!(
172 out,
173 "**Codomain:** ${}$\n",
174 crate::latex::math_to_latex(cod)
175 );
176 }
177 if !eq.invariants.is_empty() {
178 let _ = writeln!(out, "**Invariants:**\n");
179 for inv in &eq.invariants {
180 let _ = writeln!(out, "- ${}$", crate::latex::math_to_latex(inv));
181 }
182 let _ = writeln!(out);
183 }
184 if !eq.preconditions.is_empty() {
185 let _ = writeln!(out, "**Preconditions:**\n");
186 for pre in &eq.preconditions {
187 let _ = writeln!(out, "- `{pre}`");
188 }
189 let _ = writeln!(out);
190 }
191 if !eq.postconditions.is_empty() {
192 let _ = writeln!(out, "**Postconditions:**\n");
193 for post in &eq.postconditions {
194 let _ = writeln!(out, "- `{post}`");
195 }
196 let _ = writeln!(out);
197 }
198 }
199 }
200
201 if !contract.proof_obligations.is_empty() {
202 let _ = writeln!(out, "## Proof Obligations\n");
203 let _ = writeln!(out, "| # | Type | Property | Formal |");
204 let _ = writeln!(out, "|---|------|----------|--------|");
205 for (i, ob) in contract.proof_obligations.iter().enumerate() {
206 let formal = ob.formal.as_deref().unwrap_or("");
207 let _ = writeln!(
208 out,
209 "| {} | `{}` | {} | {} |",
210 i + 1,
211 ob.obligation_type,
212 ob.property,
213 formal
214 );
215 }
216 let _ = writeln!(out);
217 }
218
219 let level = compute_proof_level(contract, None);
221 let _ = writeln!(out, "## Verification\n");
222 let _ = writeln!(out, "**Proof level:** {level}\n");
223 if let Some(ref vs) = contract.verification_summary {
224 let _ = writeln!(
225 out,
226 "- Lean: {}/{} proved",
227 vs.l4_lean_proved, vs.total_obligations
228 );
229 }
230 let _ = writeln!(out, "- Kani: {} harnesses", contract.kani_harnesses.len());
231 let _ = writeln!(
232 out,
233 "- Tests: {} falsification\n",
234 contract.falsification_tests.len()
235 );
236
237 if let Some(registry) = binding {
239 let contract_file = format!("{stem}.yaml");
240 let has_bindings = contract.equations.keys().any(|eq| {
241 registry
242 .bindings
243 .iter()
244 .any(|b| b.contract == contract_file && b.equation == *eq)
245 });
246 if has_bindings {
247 let _ = writeln!(out, "## Bindings ({})\n", registry.target_crate);
248 let _ = writeln!(out, "| Equation | Status |");
249 let _ = writeln!(out, "|----------|--------|");
250 for eq_name in contract.equations.keys() {
251 let status = registry
252 .bindings
253 .iter()
254 .find(|b| b.contract == contract_file && b.equation == *eq_name)
255 .map_or("missing", |b| match b.status {
256 crate::binding::ImplStatus::Implemented => "implemented",
257 crate::binding::ImplStatus::Partial => "partial",
258 crate::binding::ImplStatus::NotImplemented => "not_implemented",
259 crate::binding::ImplStatus::Pending => "pending",
260 });
261 let _ = writeln!(out, "| {eq_name} | {status} |");
262 }
263 let _ = writeln!(out);
264 }
265 }
266
267 out
268}
269
270pub fn explain_contract_json(
272 contract: &Contract,
273 stem: &str,
274 binding: Option<&BindingRegistry>,
275) -> String {
276 let level = compute_proof_level(contract, None);
277
278 let obligations: Vec<serde_json::Value> = contract
279 .proof_obligations
280 .iter()
281 .map(|ob| {
282 let mut obj = serde_json::json!({
283 "type": ob.obligation_type.to_string(),
284 "property": ob.property,
285 "pattern": obligation_pattern(ob.obligation_type),
286 });
287 if let Some(ref f) = ob.formal {
288 obj["formal"] = serde_json::json!(f);
289 }
290 if let Some(t) = ob.tolerance {
291 obj["tolerance"] = serde_json::json!(t);
292 }
293 if let Some(ref lean) = ob.lean {
294 obj["lean"] = serde_json::json!({
295 "theorem": lean.theorem,
296 "status": lean.status.to_string(),
297 });
298 }
299 if let Some(ref req) = ob.requires {
300 obj["requires"] = serde_json::json!(req);
301 }
302 if let Some(ref phase) = ob.applies_to_phase {
303 obj["applies_to_phase"] = serde_json::json!(phase);
304 }
305 if let Some(ref parent) = ob.parent_contract {
306 obj["parent_contract"] = serde_json::json!(parent);
307 }
308 obj
309 })
310 .collect();
311
312 let json = serde_json::json!({
313 "stem": stem,
314 "version": contract.metadata.version,
315 "description": contract.metadata.description,
316 "references": contract.metadata.references,
317 "depends_on": contract.metadata.depends_on,
318 "equations": contract.equations.keys().collect::<Vec<_>>(),
319 "proof_level": level.to_string(),
320 "obligations": obligations,
321 "falsification_tests": contract.falsification_tests.len(),
322 "kani_harnesses": contract.kani_harnesses.len(),
323 "binding": binding.map(|b| {
324 let contract_file = format!("{stem}.yaml");
325 let statuses: std::collections::BTreeMap<String, String> = contract
326 .equations
327 .keys()
328 .map(|eq| {
329 let status = b.bindings.iter()
330 .find(|bi| bi.contract == contract_file && bi.equation == *eq)
331 .map_or_else(|| "missing".to_string(), |bi| bi.status.to_string());
332 (eq.clone(), status)
333 })
334 .collect();
335 serde_json::json!({
336 "target_crate": b.target_crate,
337 "equations": statuses,
338 })
339 }),
340 });
341
342 serde_json::to_string_pretty(&json).unwrap_or_else(|_| "{}".to_string())
343}
344
345fn write_header(out: &mut String, contract: &Contract, stem: &str) {
346 use crate::schema::ContractKind;
347 let kind_tag = if contract.kind() == ContractKind::Kernel {
348 String::new()
349 } else {
350 format!(" [{}]", contract.kind())
351 };
352 let _ = writeln!(out, "{stem} (v{}){kind_tag}", contract.metadata.version);
353 let _ = writeln!(out, "{}", contract.metadata.description);
354 let _ = writeln!(out);
355}
356
357fn write_what(out: &mut String, contract: &Contract) {
358 let _ = writeln!(out, "What this contract specifies");
359
360 let refs = &contract.metadata.references;
361 if refs.is_empty() {
362 let _ = writeln!(
363 out,
364 " This contract specifies {}.",
365 contract.metadata.description
366 );
367 } else {
368 let _ = write!(
369 out,
370 " This contract specifies {}. It derives from",
371 contract.metadata.description
372 );
373 for (i, r) in refs.iter().enumerate() {
374 if i == 0 {
375 let _ = write!(out, " {r}");
376 } else {
377 let _ = write!(out, " and {r}");
378 }
379 }
380 let _ = writeln!(out, ".");
381 }
382
383 if !contract.metadata.depends_on.is_empty() {
384 let _ = write!(out, " Depends on:");
385 for dep in &contract.metadata.depends_on {
386 let _ = write!(out, " {dep}");
387 }
388 let _ = writeln!(out);
389 }
390
391 let _ = writeln!(out);
392}
393
394fn write_equations(out: &mut String, contract: &Contract) {
395 if contract.equations.is_empty() {
396 return;
397 }
398 let _ = writeln!(out, "Governing equations");
399 let _ = writeln!(out);
400
401 for (name, eq) in &contract.equations {
402 let _ = writeln!(out, " {name}");
403 let _ = writeln!(out, " {}", eq.formula);
404
405 if let Some(ref dom) = eq.domain {
406 let _ = writeln!(out, " Domain: {dom}");
407 }
408 if let Some(ref cod) = eq.codomain {
409 let _ = writeln!(out, " Range: {cod}");
410 }
411
412 if !eq.invariants.is_empty() {
413 let _ = writeln!(out);
414 let _ = writeln!(out, " Invariants:");
415 for (i, inv) in eq.invariants.iter().enumerate() {
416 let _ = writeln!(out, " {}. {inv}", i + 1);
417 }
418 }
419
420 if !eq.preconditions.is_empty() {
421 let _ = writeln!(out);
422 let _ = writeln!(out, " Preconditions (caller must guarantee):");
423 for pre in &eq.preconditions {
424 let _ = writeln!(out, " - {pre}");
425 }
426 }
427
428 if !eq.postconditions.is_empty() {
429 let _ = writeln!(out);
430 let _ = writeln!(out, " Postconditions (kernel guarantees):");
431 for post in &eq.postconditions {
432 let _ = writeln!(out, " - {post}");
433 }
434 }
435
436 if let Some(ref theorem) = eq.lean_theorem {
437 let _ = writeln!(out, " Lean theorem: {theorem}");
438 }
439
440 let _ = writeln!(out);
441 }
442}