open-standards/joule-code/joule-code-rs/crates/joule-code-remediate/examples/formal_fix.rs
Transaction Science 2e88815b66
All checks were successful
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m2s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 1m39s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m14s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m20s
joule-code-remediate: formal_fix prints each proof figure with its grade, not as "measured"
The example printed each candidate's proof energy as "N µJ measured". On
the decision-procedure ladder it runs, that figure is wall time × the
proof-solvers default power, which no host calibrated: CascadeResult grades
it Estimator, and joule-code-remediate's own docs say so. The example now
prints the grade the result carries:

    UNPROVED — z3 found a counter-model ...   (590377 µJ, Estimator)
    PROVED   by Some(Smt) — ...               (199948 µJ, Estimator)

Found by a sweep of main for figures labelled "measured" that are not
readings. It was the only one left. The other hits print the grade
conditionally (energy_probe, l3-jouleclaw) or seal UNMETERED when the rail
is not live (the wai meters).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 20:01:38 -04:00

197 lines
9.4 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The **formally-verified fix tier** — the verification rung past build+test.
//!
//! `joule-fix`'s `run_once` discharges six *operational* gates (build, tests,
//! lint-delta, frame, diff). They answer "did this change break anything we
//! tested?" — not "is this change *correct*?". For a fix whose correctness is a
//! formal property, there is a stronger gate, and every piece to run it already
//! ships in this monorepo:
//!
//! - `joulecontract-proof` — the vericoding bridge: a contract obligation that
//! carries a formalization becomes a content-addressed proof goal.
//! - `proof-solvers::real_ladder()` — a cheapest-sufficient cascade ending in
//! a **real `z3`** SMT backend (assert the negation; `unsat` ⇒ valid).
//! - the proof cascade meters every attempt in joules.
//!
//! This example wires them into a fix flow the harness does not yet run inline.
//! A settlement-netting function must satisfy one postcondition:
//!
//! when debits == credits, the net is zero.
//!
//! Two candidate fixes arrive. The proof — not a test suite — decides which one
//! is correct, and the buggy one is *refused before it can ship*:
//!
//! - candidate A (`net = debits + credits`): z3 finds a counter-model
//! (debits = credits = 1 ⇒ net = 2 ≠ 0) → **Unproved** → the formal gate
//! rejects the fix, with the wasted joules on record.
//! - candidate B (`net = debits - credits`): z3 finds the negation `unsat` →
//! the postcondition holds for *all* integers → **Proved** by the SMT rung,
//! measured joules sealed.
//!
//! That is the rigor nobody in the remediation market has reached: a fix gated
//! by a machine-checked proof of its own postcondition, priced in joules. It is
//! honest by construction — where `z3` is absent both candidates come back
//! Unproved (never a fabricated pass), and the unformalized obligations are
//! demoted, never dropped.
//!
//! Run with: `cargo run -p joule-code-remediate --example formal_fix`
use joulecontract_proof::{discharge, DischargeStatus, Formalization};
use joulecontract_schema::{Ceiling, Contract, ContentAddress, Rigor};
use proof_cascade::{ProofCache, Router};
use proof_schema::{GoalKind, SpecGrounding};
use proof_solvers::{real_ladder, z3_available};
// SOUNDNESS NOTE — why this example uses a decision-procedure ladder.
//
// This example originally exposed a soundness hole: against
// `proof_solvers::real_ladder()` = `[Propositional, Z3Smt, SmallModel,
// FrontierModel]`, candidate A (the bug) came back "PROVED by BigModel" at
// ~50 J. The cascade had no `Verdict::Refuted`, so `Z3Smt` collapsed z3's `sat`
// (a real counter-model) into `Unproved`; the router escalated it; and
// `FrontierModel` proves ANY goal unconditionally — so a disproof was
// fabricated into a proof.
//
// That was fixed in the proof crate: `Verdict::Refuted` exists, `Z3Smt` emits
// it on `sat`, and the router HALTS on a refutation instead of escalating
// (regression test in proof-cascade).
//
// A SECOND hole survived that fix, and this note used to describe the local
// workaround for it: escalation past a rung that *could not run at all*. With
// `z3` absent, `Z3Smt` returned `Unproved`, the router escalated, and
// `FrontierModel` closed the goal — so on any machine without `z3`, every goal
// came back proved at ~50 J of imaginary energy. This example dodged it by
// hand-building `[Propositional, Z3Smt]` instead of calling `real_ladder()`.
//
// That is now fixed at the source rather than at this call site:
// `real_ladder()` IS the decision-procedure ladder, the stand-ins live only in
// the `reference_*` fixtures, and `Solver::is_decision_procedure` makes the
// distinction machine-checkable (`ladder_is_sound` in proof-solvers). So the
// hand-rolled ladder is gone and the guarantee is unchanged: "a decision
// procedure decided this", nothing weaker.
/// The contract over the netting function: one precondition, one postcondition
/// (the property the fix must honor), one frame. Same spec for both candidates
/// — only the fix's computed `net` differs.
fn netting_contract() -> Contract {
Contract {
joulecontract: "joulecontract/0.6".into(),
subject: "settlement netting — net(debits, credits)".into(),
rigor_target: Rigor::Jr2,
pre: vec!["debits and credits are integers".into()],
post: vec!["when debits == credits, the net is zero".into()],
inv: vec![],
frame: vec!["the settlement ledger row".into()],
ceiling: Ceiling {
enpi_joules: 10.0,
unit: "joules/settlement".into(),
enb_ref: ContentAddress::of_bytes(b"enb-settlement-2026"),
derivation: None,
},
sector_equivalence: None,
amends: None,
}
}
/// An SMT-LIB2 goal: the candidate's computed `net`, the premise `debits ==
/// credits`, and the **negation** of the postcondition. `unsat` ⇒ no
/// counter-model exists ⇒ the postcondition is a theorem for that candidate.
fn postcondition_goal(net_definition: &str) -> String {
format!(
"(declare-const debits Int)\n\
(declare-const credits Int)\n\
(declare-const net Int)\n\
(assert (= net {net_definition}))\n\
(assert (= debits credits))\n\
(assert (not (= net 0)))"
)
}
/// Discharge the post-condition (obligation index 1: pre=0, post=1, frame=2)
/// for one candidate fix, and report the verdict and the joules the cascade
/// accounted for it, with their grade. On this ladder that grade is
/// `Estimator` (wall time × proof-solvers' uncalibrated default power), so the
/// figure is printed as an estimate, never as a measurement.
fn judge(label: &str, net_definition: &str) -> bool {
let contract = netting_contract();
let formalization = Formalization {
obligation: 1, // the postcondition
statement: postcondition_goal(net_definition),
kind: GoalKind::Decidable,
grounding: SpecGrounding::CrossChecked,
};
// A fresh cascade + cache per candidate so the joules reported are this
// candidate's own proof work, not a cache hit from the other. `real_ladder`
// is decision-procedures only (see the SOUNDNESS NOTE at the top) — a formal
// gate may not fall through to a model stand-in that fabricates a proof.
let router = Router::new(real_ladder());
let mut cache = ProofCache::new();
let d = discharge(&contract, &[formalization], &router, &mut cache)
.expect("discharge runs (bookkeeping is well-formed)");
let entry = &d.entries[1];
println!("── candidate {label}: net = {net_definition}");
match &entry.status {
DischargeStatus::Proved(result) => {
println!(
" PROVED by {:?} — postcondition holds for all integers ({} µJ, {:?})",
result.closed_by, result.total_uj, result.provenance
);
println!(" → formal gate ACCEPTS this fix.");
true
}
DischargeStatus::Unproved(result) => {
// "Unproved" has two very different causes and they must not read
// the same: z3 ran and refuted the claim, or z3 was never there to
// ask. Printing "found a counter-model" in the second case would
// report evidence that was never gathered — the exact failure this
// example exists to demonstrate.
let why = if z3_available() {
"z3 found a counter-model; the postcondition can fail"
} else {
"no decision procedure available to decide it (z3 absent)"
};
println!(" UNPROVED — {why} ({} µJ, {:?})", result.total_uj, result.provenance);
println!(" → formal gate REFUSES this fix (it would be reverted, never shipped).");
false
}
DischargeStatus::Unformalized => {
println!(" UNFORMALIZED — no proof goal supplied (recorded, never dropped).");
false
}
}
}
fn main() {
println!("formally-verified fix tier — settlement netting postcondition\n");
println!("postcondition under proof: debits == credits ⇒ net == 0\n");
if !z3_available() {
println!(
"note: `z3` is not on PATH — both candidates will come back UNPROVED.\n\
That is the honesty rule, not a failure: the harness never fabricates\n\
a proof it could not run. Install z3 to see the SMT rung discharge.\n"
);
}
let buggy_ok = judge("A (the bug)", "(+ debits credits)");
println!();
let fixed_ok = judge("B (the fix)", "(- debits credits)");
println!("\n── verdict");
if z3_available() {
assert!(!buggy_ok, "the buggy candidate must NOT pass the formal gate");
assert!(fixed_ok, "the correct fix must pass the formal gate");
println!(
"the proof discriminated: candidate A refused, candidate B verified.\n\
the shipped fix is gated by a machine-checked proof of its postcondition —\n\
a rung above build+test, priced in measured joules. that is the tier the\n\
remediation market has not reached."
);
} else {
println!(
"z3 absent: both candidates Unproved (honest). the gate refused the bug AND\n\
the fix — refusing a correct fix you could not prove beats shipping an\n\
unproven one. with z3 present, candidate B verifies and candidate A is refused."
);
}
}