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
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]>
197 lines
9.4 KiB
Rust
197 lines
9.4 KiB
Rust
//! 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."
|
||
);
|
||
}
|
||
}
|