open-standards/joule-code/joule-code-rs/crates/joule-code-joulecontract/src/lib.rs
Transaction Science b1a13c4e35
Some checks failed
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m34s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m21s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 19s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 3s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 1m33s
joule-code-joulecontract: an obligation nothing checked is not discharged
`receipt_for_op` computed one obligation and sealed the rest as
discharged:

    result: if is_frame { frame_result.clone() } else { DischargeResult::Closed },

`Closed` is JouleContract's positive "this obligation was discharged".
Every Pre, Post and Inv obligation `contract_for_op` declares was
sealed with it — attributed to a named `TierClass::Deterministic` tier,
carrying the binder's verifier keys, qualification and engagement, and
signed — although no code evaluated any of them. The crate doc presents
this binding as "not an analogy; the objects line up field by field".

The sharpest case is the Post obligation, because it states the *same
property* as the Frame obligation. The crate's own test
`out_of_frame_edit_is_caught_at_the_boundary` produced a receipt
carrying, in one `discharge` vector:

    Frame: Undischarged { reason: "out-of-frame edit: deploy/secrets.env" }
    Post ("every edit applied within the declared frame"): Closed

One signature asserting both that an edit escaped the frame and that
every edit stayed inside it. `validate` reported only the frame
demotion, so a relying party reading the Post entry saw a passed check
that was never run.

Every obligation `contract_for_op` writes is now decided, and all four
are decidable from primitives that already existed:

- **Post** "every edit applied within the declared frame" carries the
  frame verdict — same property, same evidence.
- **Inv** "op signature verifies over op_id ‖ prior" is exactly what
  `SignedOp::verify` decides: it recomputes `op_id`, rebuilds
  `op_id ‖ prior`, checks the signature against `op.author`, and rejects
  replay onto a different base.
- **Inv** "edits are ordered and pairwise disjoint" is exactly what
  `CodingOp::validate` decides (SPEC §2).
- **Pre** "op builds on prior tree X" checks the contract was written
  for the prior this receipt states, so the contract and the receipt
  cannot disagree silently.
- Anything else a caller declared is `Undischarged { "not evaluated by
  this binder: …" }`. **There is no default `Closed`** — that
  fall-through was the defect.

Two signature changes make the undecidable decidable, and
`receipt_for_op` has no callers outside its own tests:

- It takes `&SignedOp` and the `prior` the verifier believes current,
  because two obligations are statements about exactly those. With an
  unsigned `CodingOp` neither could be decided — and both were sealed
  as discharged anyway.
- The op id is **derived** from the op instead of supplied. A
  caller-supplied `op_id` was trusted and never re-derived, so
  `transition: bridge_id(op_id)` could name a different operation than
  the one whose frame was checked.

The fixture told the same story: it set `author: [7u8; 32]`, which is
not a key, and returned `ContentId::of_bytes(b"the op canonical bytes")`
as the op id — a constant unrelated to the op. It now signs with a real
key and sorts the edits into canonical order, because `op_id` runs
`validate` and `sign` runs `op_id`.

`in_frame_op_discharges_and_validates_to_jr2` still passes unchanged:
an honest check of a valid in-frame signed op closes all four
obligations, so nothing was traded away to stop fabricating them.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-17 16:08:58 -04:00

650 lines
26 KiB
Rust

//! A Joule Code coding operation under a JouleContract.
//!
//! The mapping is not an analogy; the objects line up field by field:
//!
//! - **The edit set is the frame.** A `CodingOp`'s `edits[].path` list is the
//! declared mutable footprint (JouleContract §3.4). An edit outside the
//! contracted paths is an out-of-frame effect — the breach primitive, made
//! concrete for code, caught at the boundary by [`frame_check`].
//! - **The tier cost is the ceiling.** The cascade's per-tier joule class
//! prices the contract's EnPI ceiling.
//! - **JCR provenance maps onto the energy classes.** `HwShunt` → `Metered`
//! (with a meter binding), `ModelBased` → `ModelBased` (with an anchor),
//! `Estimator` → `Unaccounted`. Honest in, honest out — an `Estimator`
//! figure can never serialize as `Metered` through this binding.
//!
//! The binding produces receipts that pass JouleContract's **receipt-local**
//! validation (at most JR-2 — JouleContract SPEC §10's two-stage rule);
//! composing them to JR-3+ with sealed evidence, a key log, and anchored
//! intervals is the deployment's verifier-of-record work, not this crate's.
use joule_code_cargo::{CodingOp, SignedOp};
use joule_code_receipt::{Energy, Provenance};
use joule_code_schema::ContentId;
use joulecontract_receipt::{
DischargeEntry, DischargeResult, Engagement, FrameMode, Generator, KeyId, MeterBinding,
Qualification, Receipt, TierClass,
};
use joulecontract_schema::{
Ceiling, ContentAddress, Contract, DerivationBinding, EnergyClass, GovernanceClass, Profile,
Rigor,
};
use std::collections::BTreeSet;
/// Microjoules per joule: JCR energy records are integer µJ; JouleContract
/// ceilings are EOC joules.
const UJ_PER_J: f64 = 1_000_000.0;
/// Bridge a Joule Code content identity (`b3:…`, BLAKE3/base64) into a
/// JouleContract content address (`sha2-256:…`). The foreign identity string
/// is the addressed content — a cross-standard *reference*, not a re-hash
/// claim: resolving it back to the Joule Code object goes through the
/// original `ContentId`.
pub fn bridge_id(id: &ContentId) -> ContentAddress {
ContentAddress::of_bytes(id.0.as_bytes())
}
/// JCR provenance → JouleContract energy class. The mapping only ever moves
/// trust *down*: a coarse estimator is `Unaccounted`, never promoted.
pub fn energy_class(p: Provenance) -> EnergyClass {
match p {
Provenance::HwShunt => EnergyClass::Metered,
Provenance::ModelBased => EnergyClass::ModelBased,
Provenance::Estimator => EnergyClass::Unaccounted,
}
}
/// Build the JouleContract for a coding request: what must hold, what may be
/// touched, what may be spent.
pub fn contract_for_op(
intent: &str,
prior: &ContentId,
frame_paths: &[String],
ceiling_joules: f64,
enb_ref: ContentAddress,
derivation: Option<DerivationBinding>,
rigor_target: Rigor,
) -> Contract {
Contract {
joulecontract: "0.8.0".into(),
subject: intent.to_string(),
rigor_target,
pre: vec![format!("op builds on prior tree {}", prior.0)],
post: vec!["every edit applied within the declared frame".into()],
inv: vec![
"op signature verifies over op_id ‖ prior".into(),
"edits are ordered and pairwise disjoint".into(),
],
frame: frame_paths.to_vec(),
ceiling: Ceiling {
enpi_joules: ceiling_joules,
unit: "EOC-J".into(),
enb_ref,
derivation,
},
sector_equivalence: None,
amends: None,
}
}
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum BindingError {
/// An edit outside the contracted footprint: the out-of-frame effect,
/// caught at the boundary instead of discovered in review.
#[error("out-of-frame edit: `{path}` is not within the contracted footprint")]
OutOfFrame { path: String },
#[error("contract addressing failed: {0}")]
Address(String),
/// The op's own id could not be derived, so no receipt can name the
/// operation it is about.
#[error("op id could not be derived: {0}")]
OpId(String),
}
/// Decide one declared obligation against what this binder actually holds.
///
/// Every arm either evaluates the obligation or reports it `Undischarged`
/// with the reason it was not evaluated. **There is no default `Closed`.**
/// `Closed` is JouleContract's positive "this obligation was discharged", and
/// it used to be the fall-through for every Pre, Post and Inv obligation —
/// signed, attributed to a named `Deterministic` tier, with nothing having
/// evaluated them. The worst case was the Post obligation below: it states
/// the same property as the frame obligation, so a single receipt could carry
/// `Frame = Undischarged { "out-of-frame edit: deploy/secrets.env" }` and
/// `Post = Closed` for "every edit applied within the declared frame" — one
/// signature asserting both that an edit escaped the frame and that every
/// edit stayed inside it.
fn discharge_for(
signed: &SignedOp,
prior: &ContentId,
frame_result: &DischargeResult,
ob: &joulecontract_schema::Obligation,
) -> DischargeResult {
use joulecontract_schema::ObligationKind as Kind;
match (&ob.kind, ob.text.as_str()) {
// Decided by `frame_check`.
(Kind::Frame, _) => frame_result.clone(),
// The same property as the frame obligation, so it carries the same
// evidence rather than a second, contradictory verdict.
(Kind::Post, "every edit applied within the declared frame") => frame_result.clone(),
// The contract names the tree the op must build on. `SignedOp::verify`
// below binds the op to `prior`; this checks the *contract* was
// written for that same prior, so the two cannot disagree silently.
(Kind::Pre, text) if text.starts_with("op builds on prior tree ") => {
let expected = format!("op builds on prior tree {}", prior.0);
if text == expected {
DischargeResult::Closed
} else {
DischargeResult::Undischarged {
reason: format!("contract declares `{text}`, this receipt states prior {}", prior.0),
}
}
}
// Exactly what `SignedOp::verify` decides: it recomputes `op_id`,
// rebuilds `op_id ‖ prior`, checks the signature against `op.author`,
// and rejects replay onto a different base.
(Kind::Inv, "op signature verifies over op_id ‖ prior") => match signed.verify(prior) {
Ok(()) => DischargeResult::Closed,
Err(e) => DischargeResult::Undischarged {
reason: format!("signature does not verify over op_id ‖ prior: {e}"),
},
},
// Exactly what `CodingOp::validate` decides (SPEC §2: known alg,
// non-empty edits, ascending `(path, start)`, no overlap).
(Kind::Inv, "edits are ordered and pairwise disjoint") => match signed.op.validate() {
Ok(()) => DischargeResult::Closed,
Err(e) => DischargeResult::Undischarged { reason: e.to_string() },
},
// An obligation this binder does not recognise. A caller may declare
// any prose it likes; none of it is discharged by being written down.
(_, text) => DischargeResult::Undischarged {
reason: format!("not evaluated by this binder: {text}"),
},
}
}
/// Check every edit path against the contract's frame. A path is in-frame
/// when it equals a frame entry or sits under a frame entry treated as a
/// directory prefix (`src/` covers `src/lib.rs`).
pub fn frame_check(contract: &Contract, op: &CodingOp) -> Result<(), BindingError> {
for edit in &op.edits {
let in_frame = contract.frame.iter().any(|entry| {
let dir = if entry.ends_with('/') {
entry.clone()
} else {
format!("{entry}/")
};
edit.path == *entry || edit.path.starts_with(&dir)
});
if !in_frame {
return Err(BindingError::OutOfFrame {
path: edit.path.clone(),
});
}
}
Ok(())
}
/// The static identities a deployment binds once: who verifies, under what
/// qualification, paid by whom; who generates; where the meter attestation
/// comes from when the platform has one.
pub struct Binder {
pub verifier_keys: BTreeSet<KeyId>,
pub verifier_entity: Option<String>,
pub engagement: Engagement,
pub recourse: Option<String>,
pub qualification: Qualification,
pub tier_operator_keys: BTreeSet<KeyId>,
pub generator: Generator,
pub receipt_signer: KeyId,
/// Meter attestation for `HwShunt` readings (`Metered` binds or demotes).
pub meter: Option<MeterBinding>,
/// Calibration anchor for `ModelBased` readings.
pub energy_anchor: Option<ContentAddress>,
pub register_pin: ContentAddress,
/// The sealed evidence bundle address, where the deployment seals one.
pub evidence: Option<ContentAddress>,
}
impl Binder {
/// Discharge a coding operation against its contract into a JouleContract
/// receipt. The frame is checked **here** — its discharge mode is
/// `Observed` because the footprint was compared against the actual edit
/// set, not analyzed from the artifact. An out-of-frame op still gets a
/// receipt: the frame serializes `undischarged` with the offending path,
/// because a breach recorded honestly beats a breach driven underground.
/// Seal a receipt for one signed op against a contract.
///
/// Takes the **signed** op and the `prior` the verifier believes current,
/// because two of the contract's obligations are statements about exactly
/// those: the signature over `op_id ‖ prior`, and the tree the op builds
/// on. With an unsigned `CodingOp` and a caller-supplied `op_id` neither
/// could be decided — and both were nonetheless sealed as discharged.
///
/// The op id is **derived** from the op rather than supplied, so
/// `transition` always names the operation whose obligations were checked;
/// a caller-supplied id was trusted and never re-derived, so a receipt
/// could carry one op's frame verdict under another op's id.
///
/// # Errors
/// [`BindingError::OpId`] if the op's id cannot be derived (it is
/// structurally invalid), or [`BindingError::Address`] if the contract
/// cannot be addressed. A failing *obligation* is not an error: it is
/// recorded as `Undischarged` on the receipt, which is the point.
pub fn receipt_for_op(
&self,
contract: &Contract,
signed: &SignedOp,
prior: &ContentId,
energy: &Energy,
) -> Result<Receipt, BindingError> {
let op: &CodingOp = &signed.op;
let op_id = op.op_id().map_err(|e| BindingError::OpId(e.to_string()))?;
let frame_result = match frame_check(contract, op) {
Ok(()) => DischargeResult::Closed,
Err(BindingError::OutOfFrame { path }) => DischargeResult::Undischarged {
reason: format!("out-of-frame edit: {path}"),
},
Err(e) => return Err(e),
};
let provenance = energy_class(energy.provenance);
let spent_uj = energy.measured_uj.unwrap_or(energy.estimated_uj);
let joules_spent = spent_uj as f64 / UJ_PER_J;
let binding_tier = ContentAddress::of_bytes(b"joule-code-joulecontract deterministic binding checks v1");
let discharge = contract
.obligations()
.into_iter()
.map(|ob| {
let is_frame = ob.kind == joulecontract_schema::ObligationKind::Frame;
let result = discharge_for(signed, prior, &frame_result, &ob);
DischargeEntry {
obligation: ob,
tier: binding_tier.clone(),
tier_class: TierClass::Deterministic,
tier_operator_keys: self.tier_operator_keys.clone(),
result,
mode: is_frame.then_some(FrameMode::Observed),
qualification: Some(self.qualification.clone()),
verifier_keys: self.verifier_keys.clone(),
verifier_entity: self.verifier_entity.clone(),
engagement: Some(self.engagement.clone()),
recourse: self.recourse.clone(),
re_discharged_by: None,
}
})
.collect();
Ok(Receipt {
contract: contract
.content_address()
.map_err(|e| BindingError::Address(e.to_string()))?,
transition: bridge_id(&op_id),
generator: self.generator.clone(),
discharge,
execution: vec![ContentAddress::of_bytes(
format!("joule-code tier {:?}", op.tier).as_bytes(),
)],
joules_spent,
meter: match provenance {
EnergyClass::Metered => self.meter.clone(),
_ => None,
},
energy_anchor: match provenance {
EnergyClass::ModelBased => self.energy_anchor.clone(),
_ => None,
},
joules_allowed: contract.ceiling.enpi_joules,
provenance,
enb_ref: contract.ceiling.enb_ref.clone(),
grid_mix: None,
register_pin: self.register_pin.clone(),
evidence: self.evidence.clone(),
claimed_profile: Profile {
rigor: Rigor::Jr2,
energy: provenance,
governance: GovernanceClass::Managed,
},
receipt_signer: self.receipt_signer.clone(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use joule_code_cargo::{Anchor, CodingOp, Edit};
use joule_code_schema::Tier;
use joulecontract_receipt::{validate, LoopGovernance, TrustRoot};
use joulecontract_schema::Demotion;
fn key(label: &str) -> KeyId {
ContentAddress::of_bytes(label.as_bytes())
}
fn author_key() -> ed25519_dalek::SigningKey {
ed25519_dalek::SigningKey::from_bytes(&[42u8; 32])
}
/// The tree `contract()` declares its op must build on.
fn prior() -> ContentId {
ContentId::of_bytes(b"prior tree")
}
/// A real signed op. `author` is the signing key's own verifying key, and
/// the edits are sorted into canonical `(path, start)` order, because
/// `op_id` runs `validate` and `sign` runs `op_id` — an op that is not
/// canonical has no id and therefore no signature.
///
/// This replaces a fixture that set `author: [7u8; 32]` (not a key) and
/// returned `ContentId::of_bytes(b"the op canonical bytes")` as the id —
/// a constant unrelated to the op, which is what let the untrusted
/// `op_id` parameter go unnoticed.
fn op(paths: &[&str]) -> SignedOp {
let key = author_key();
let mut sorted: Vec<&&str> = paths.iter().collect();
sorted.sort();
let op = CodingOp {
alg: 0x01,
prior: prior(),
edits: sorted
.into_iter()
.map(|p| Edit {
path: (*p).to_string(),
anchor: Anchor::ByteRange { start: 0, end: 4 },
replacement: b"new!".to_vec(),
})
.collect(),
author: key.verifying_key().to_bytes(),
intent: "rename the settlement index field".into(),
tier: Tier::L1,
};
op.sign(&key).expect("a canonical op signs")
}
fn binder() -> Binder {
Binder {
verifier_keys: [key("audit-a")].into(),
verifier_entity: Some("vLEI:AUDIT-A".into()),
engagement: Engagement {
selected_by: "did:web:relying.example".into(),
compensated_by: "did:web:relying.example".into(),
},
recourse: Some("ISAE3000".into()),
qualification: Qualification {
attestation: key("binding qualification"),
signer_key: key("assessor"),
valid_until: 20_000,
validated_at: 10_000,
root: Some(TrustRoot::ReferenceImpl),
},
tier_operator_keys: [key("binding-operator")].into(),
generator: Generator {
system: "joule-code cascade".into(),
version_digest: key("cascade build"),
attestation: Some(key("cascade attestation")),
attestation_signer: Some(key("registry")),
key_set: [key("producer")].into(),
entity: Some("vLEI:PRODUCER".into()),
human_principals: vec!["did:web:eng.example".into()],
loop_governance: Some(LoopGovernance {
contract: key("loop contract"),
discharged_by: [key("audit-a")].into(),
root: TrustRoot::HumanAttested,
}),
},
receipt_signer: key("producer"),
meter: Some(MeterBinding {
attestation: key("rapl attestation"),
signer_key: key("substrate-telemetry"),
valid_until: 20_000,
validated_at: 10_000,
}),
energy_anchor: Some(key("calibration anchor")),
register_pin: key("register snapshot"),
evidence: None,
}
}
fn contract(frame: &[&str]) -> Contract {
contract_for_op(
"rename the settlement index field",
&ContentId::of_bytes(b"prior tree"),
&frame.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
0.01,
key("enb"),
Some(DerivationBinding {
attestation: key("derivation"),
basis: "metered median of comparable L1 ops".into(),
signer: "did:web:energy-office.example".into(),
}),
Rigor::Jr2,
)
}
#[test]
fn in_frame_op_discharges_and_validates_to_jr2() {
let c = contract(&["src/", "Cargo.toml"]);
let signed = op(&["src/lib.rs", "Cargo.toml"]);
let energy = Energy {
measured_uj: Some(1_200),
estimated_uj: 1_000,
provenance: Provenance::HwShunt,
};
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
let v = validate(&c, &r).unwrap();
assert_eq!(v.max_profile.rigor, Rigor::Jr2);
assert_eq!(v.max_profile.energy, EnergyClass::Metered);
assert!(v.permits(&r.claimed_profile), "demotions: {:?}", v.demotions);
assert!((r.joules_spent - 0.0012).abs() < 1e-9);
assert!(r.residual() > 0.0);
}
#[test]
fn out_of_frame_edit_is_caught_at_the_boundary() {
let c = contract(&["src/"]);
let signed = op(&["src/lib.rs", "deploy/secrets.env"]);
assert_eq!(
frame_check(&c, &signed.op),
Err(BindingError::OutOfFrame {
path: "deploy/secrets.env".into()
})
);
// The receipt still issues — with the frame undischarged and the
// offending path on the record. Honest breach beats hidden breach.
let energy = Energy {
measured_uj: None,
estimated_uj: 900,
provenance: Provenance::ModelBased,
};
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
let v = validate(&c, &r).unwrap();
assert!(v.demotions.iter().any(|d| matches!(
d,
Demotion::Undischarged { reason } if reason.contains("deploy/secrets.env")
)));
assert_eq!(v.max_profile.rigor, Rigor::Jr1, "undischarged frame caps rigor");
}
/// The contradiction this fix removes. The Post obligation states the same
/// property as the Frame obligation, so one receipt cannot carry
/// "an edit escaped the frame" and "every edit stayed inside it" at once.
#[test]
fn the_post_obligation_carries_the_frame_verdict() {
let c = contract(&["src/"]);
let signed = op(&["src/lib.rs", "deploy/secrets.env"]);
let energy = Energy {
measured_uj: Some(900),
estimated_uj: 900,
provenance: Provenance::HwShunt,
};
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
let post = r
.discharge
.iter()
.find(|d| d.obligation.text == "every edit applied within the declared frame")
.expect("the contract declares it");
let frame = r
.discharge
.iter()
.find(|d| d.obligation.kind == joulecontract_schema::ObligationKind::Frame)
.expect("the contract declares it");
assert_eq!(post.result, frame.result, "same property, same verdict");
assert!(
matches!(&post.result, DischargeResult::Undischarged { reason } if reason.contains("deploy/secrets.env")),
"got {:?}",
post.result
);
}
/// The signature obligation is decided, not assumed. A tampered signature
/// leaves it undischarged instead of sealing "verifies" over bytes that
/// do not.
#[test]
fn the_signature_obligation_is_decided_not_assumed() {
let c = contract(&["src/"]);
let mut signed = op(&["src/lib.rs"]);
let energy = Energy {
measured_uj: Some(900),
estimated_uj: 900,
provenance: Provenance::HwShunt,
};
// Intact: the obligation closes on a real check.
let good = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
let sig_of = |r: &Receipt| {
r.discharge
.iter()
.find(|d| d.obligation.text == "op signature verifies over op_id \u{2016} prior")
.map(|d| d.result.clone())
.expect("the contract declares it")
};
assert_eq!(sig_of(&good), DischargeResult::Closed);
// Tampered: undischarged, with the reason on the record.
*signed.sig.last_mut().expect("signature bytes") ^= 1;
let bad = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
assert!(
matches!(sig_of(&bad), DischargeResult::Undischarged { .. }),
"got {:?}",
sig_of(&bad)
);
}
/// A caller may declare any prose it likes; none of it is discharged by
/// being written down. The fall-through used to be `Closed`.
#[test]
fn an_unrecognised_obligation_is_never_closed() {
let mut c = contract(&["src/"]);
c.post.push("the author was well rested".into());
let signed = op(&["src/lib.rs"]);
let energy = Energy {
measured_uj: Some(900),
estimated_uj: 900,
provenance: Provenance::HwShunt,
};
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
let entry = r
.discharge
.iter()
.find(|d| d.obligation.text == "the author was well rested")
.expect("declared");
assert!(
matches!(&entry.result, DischargeResult::Undischarged { reason } if reason.contains("not evaluated")),
"got {:?}",
entry.result
);
}
/// The receipt names the operation whose obligations it checked: the id is
/// derived from the op, not supplied beside it.
#[test]
fn the_transition_names_the_op_it_checked() {
let c = contract(&["src/"]);
let signed = op(&["src/lib.rs"]);
let energy = Energy {
measured_uj: Some(900),
estimated_uj: 900,
provenance: Provenance::HwShunt,
};
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
assert_eq!(r.transition, bridge_id(&signed.op.op_id().unwrap()));
}
/// A contract written for a different tree does not have its precondition
/// discharged by a receipt that states another prior.
#[test]
fn a_contract_for_a_different_prior_leaves_the_precondition_open() {
let c = contract_for_op(
"rename the settlement index field",
&ContentId::of_bytes(b"some other tree"),
&["src/".to_string()],
0.01,
key("enb"),
None,
Rigor::Jr2,
);
let signed = op(&["src/lib.rs"]);
let energy = Energy {
measured_uj: Some(900),
estimated_uj: 900,
provenance: Provenance::HwShunt,
};
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
let pre = r
.discharge
.iter()
.find(|d| d.obligation.kind == joulecontract_schema::ObligationKind::Pre)
.expect("declared");
assert!(
matches!(&pre.result, DischargeResult::Undischarged { .. }),
"got {:?}",
pre.result
);
}
#[test]
fn estimator_energy_is_unaccounted_never_promoted() {
let c = contract(&["src/"]);
let signed = op(&["src/lib.rs"]);
let energy = Energy::from_tokens(100, 10);
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
assert_eq!(r.provenance, EnergyClass::Unaccounted);
assert!(r.meter.is_none(), "an estimator figure never carries a meter binding");
let v = validate(&c, &r).unwrap();
assert_eq!(v.max_profile.energy, EnergyClass::Unaccounted);
}
#[test]
fn overrun_is_recorded_not_blocked() {
// Receipt-time enforcement (JouleContract §5.3): the deviation is on
// the record as a negative residual; amend-or-fix is the resolution.
let c = contract(&["src/"]);
let signed = op(&["src/lib.rs"]);
let energy = Energy {
measured_uj: Some(50_000), // 0.05 J against a 0.01 J ceiling
estimated_uj: 50_000,
provenance: Provenance::HwShunt,
};
let r = binder().receipt_for_op(&c, &signed, &prior(), &energy).unwrap();
assert!(r.residual() < 0.0);
assert!(validate(&c, &r).is_ok(), "the system keeps running; the record decides");
}
#[test]
fn provenance_mapping_is_total_and_downward_honest() {
assert_eq!(energy_class(Provenance::HwShunt), EnergyClass::Metered);
assert_eq!(energy_class(Provenance::ModelBased), EnergyClass::ModelBased);
assert_eq!(energy_class(Provenance::Estimator), EnergyClass::Unaccounted);
}
}