From 50b5efaad0e24e0769c7ab1697a3fdcc8cfb1b69 Mon Sep 17 00:00:00 2001 From: David Charlot Date: Thu, 3 Sep 2026 15:06:42 -0400 Subject: [PATCH] jcp: count the joules a cache hit did not spend, without ever calling it spend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MCP's 2026-07-28 revision added `CacheableResult` with `ttlMs`/`cacheScope`, so a served result can declare itself reusable. When it is reused, work that would have run did not — and for a family whose doctrine is that inference is the last resort, the cheapest joule is the one never spent. Nothing in the family bound a reused result to an energy figure. It is also the easiest quantity here to lie with, so it does not become another `Energy`. **It is a counterfactual, not a measurement.** Nothing measures a joule that was not spent; an avoided figure is a claim about what would have happened. The only thing making it checkable is a pointer to the execution it stands in for, so `AvoidedEnergy` cannot be constructed without naming the receipt whose measured cost it claims to have avoided. Without that, "this hit saved 10 mJ" is unfalsifiable — and unfalsifiable savings are how an efficiency programme reports progress it never made. **It must never be added to energy spent.** If avoided joules were summed with spent joules, or subtracted from them, a system could manufacture arbitrary efficiency and, with enough hits, report negative consumption. Physics has no negative-joule case; an accounting system that does has a bug that looks like a triumph. So this type produces no `Energy`, offers no arithmetic against a spend total, and `AvoidedSummary::saving_ppm_of` takes the spend as an argument rather than holding it — the two totals meet in exactly one place, expressed as a ratio *to* spend and never a deduction *from* it. A test drives five million-microjoule hits against a 200 µJ spend and shows no path to a negative spend figure. **A cache hit is not free.** The net subtracts what serving the hit cost, since claiming the full prior cost over-claims by exactly that amount. When a lookup costs more than recomputing would have, the saving is reported **negative rather than clamped**: a cache that does not pay for itself is a real outcome, and rounding it to "no saving" hides the one case an operator most needs to act on. `unprofitable_hits` is counted separately, so a cache that is net-positive overall while losing on most requests is visible rather than averaged away. The saving inherits the basis measurement's acquisition class, because a saving standing in for an `Estimator` figure is an estimated saving however precisely it prints. Ranked by the 2026-08-29 sweep as the one remaining item it called unclaimed territory rather than an adapter. Verified: 265 jcp-rs tests pass (5 new), conformance passes all classes, no clippy warnings in the new file. Co-Authored-By: Claude Opus 5 --- .../crates/jcp-efficiency/src/avoided.rs | 283 ++++++++++++++++++ jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs | 1 + 2 files changed, 284 insertions(+) create mode 100644 jcp/jcp-rs/crates/jcp-efficiency/src/avoided.rs diff --git a/jcp/jcp-rs/crates/jcp-efficiency/src/avoided.rs b/jcp/jcp-rs/crates/jcp-efficiency/src/avoided.rs new file mode 100644 index 000000000..d4ba848a8 --- /dev/null +++ b/jcp/jcp-rs/crates/jcp-efficiency/src/avoided.rs @@ -0,0 +1,283 @@ +//! Joules a cache hit did not spend — a counterfactual, kept apart from measurement. +//! +//! # Why this needs a type of its own +//! +//! MCP's 2026-07-28 revision added `CacheableResult` with `ttlMs` and `cacheScope`, so +//! a served result can now declare itself reusable. When it is reused, work that would +//! have run did not, and that is a real and interesting quantity: for a family whose +//! whole doctrine is "inference is the last resort", the cheapest joule is the one never +//! spent. +//! +//! It is also the easiest quantity in the system to lie with, which is why it does not +//! simply become another `Energy`. +//! +//! # Avoided energy is a counterfactual, not a measurement +//! +//! Nothing measures a joule that was not spent. An avoided figure is a claim about what +//! *would* have happened, and the only thing that makes it checkable is a pointer to the +//! execution it is standing in for. So [`AvoidedEnergy`] cannot be constructed without +//! naming the receipt whose measured cost it claims to have avoided. Without that, "this +//! hit saved 10 mJ" is unfalsifiable — and unfalsifiable savings are how an efficiency +//! programme reports progress it never made. +//! +//! # It must never be added to energy spent +//! +//! The sharp hazard. If avoided joules were summed alongside spent joules, or subtracted +//! from them, a system could manufacture arbitrary efficiency — and with enough cache +//! hits, report negative consumption. Physics does not have a negative-joule case; an +//! accounting system that does has a bug that looks like a triumph. +//! +//! So this type deliberately produces no `Energy`, exposes no arithmetic against a spend +//! total, and is named for what it is. A rollup that wants both reports them as **two +//! figures**, never one. +//! +//! # A cache hit is not free +//! +//! Looking up, validating and returning a cached result costs something. Claiming the +//! full prior cost as "avoided" over-claims by exactly that amount, so the net figure +//! subtracts it — and when a lookup costs more than recomputing would have, the saving +//! is reported as negative rather than clamped to zero. A cache that does not pay for +//! itself is a real and worth-knowing outcome, and rounding it up to "no saving" hides +//! the one case an operator most needs to see. + +use jcp_schema::ContentId; + +/// Errors from constructing an avoided-energy claim. +#[derive(Debug, PartialEq, Eq)] +pub enum AvoidedError { + /// The basis names no measured cost, so there is nothing to have avoided. + ZeroBasis, + /// The basis figure carries no acquisition class, so it is not comparable to + /// anything — including to the spend it is standing in for. + NoBasisProvenance, +} + +impl core::fmt::Display for AvoidedError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + AvoidedError::ZeroBasis => { + write!(f, "a basis of 0 uJ avoids nothing; there is no claim to make") + } + AvoidedError::NoBasisProvenance => { + write!(f, "the basis measurement must carry its acquisition class") + } + } + } +} + +impl std::error::Error for AvoidedError {} + +/// Joules a cache hit did not spend, and the execution that figure stands in for. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AvoidedEnergy { + /// The receipt whose measured cost this hit claims to have avoided. Required: + /// it is the only thing that makes the claim checkable, and a verifier can + /// resolve it and read the figure for itself. + pub basis_receipt: ContentId, + /// The measured microjoules recorded in that receipt. + pub basis_uj: u64, + /// The acquisition class of **that** measurement — carried because the saving + /// inherits it. A saving standing in for an `Estimator` figure is an estimated + /// saving, however precisely it is printed. + pub basis_provenance: String, + /// What serving the hit actually cost. A lookup is not free. + pub hit_cost_uj: u64, + /// The TTL the cached result was served under, from `CacheableResult.ttlMs`. + /// Carried so a reader can see the window the reuse was authorised for. + pub ttl_ms: u64, +} + +impl AvoidedEnergy { + /// Claim the joules a hit avoided, against the receipt it reused. + pub fn against( + basis_receipt: ContentId, + basis_uj: u64, + basis_provenance: impl Into, + hit_cost_uj: u64, + ttl_ms: u64, + ) -> Result { + if basis_uj == 0 { + return Err(AvoidedError::ZeroBasis); + } + let basis_provenance = basis_provenance.into(); + if basis_provenance.trim().is_empty() { + return Err(AvoidedError::NoBasisProvenance); + } + Ok(AvoidedEnergy { basis_receipt, basis_uj, basis_provenance, hit_cost_uj, ttl_ms }) + } + + /// The net saving in microjoules: what the prior execution measured, less what + /// serving the hit cost. + /// + /// Signed on purpose. A negative result means the cache cost more than the work + /// it replaced, which is a real outcome — and clamping it to zero would erase + /// the single case an operator most needs to act on. + pub fn net_avoided_uj(&self) -> i128 { + i128::from(self.basis_uj) - i128::from(self.hit_cost_uj) + } + + /// Whether the cache actually paid for itself on this hit. + pub fn paid_for_itself(&self) -> bool { + self.net_avoided_uj() > 0 + } + + /// The wire form. Deliberately not an `Energy`: this figure must never flow + /// into a field that means *spent*, and giving it the spend type is how that + /// happens by accident. + pub fn to_json(&self) -> String { + format!( + "{{\"kind\":\"avoided\",\"basis_receipt\":\"{}\",\"basis_uj\":{},\ + \"basis_provenance\":\"{}\",\"hit_cost_uj\":{},\"net_avoided_uj\":{},\ + \"ttl_ms\":{}}}", + self.basis_receipt.to_hex(), + self.basis_uj, + self.basis_provenance, + self.hit_cost_uj, + self.net_avoided_uj(), + self.ttl_ms + ) + } +} + +/// A period's cache savings, reported **beside** energy spent and never merged into +/// it. +/// +/// The two totals answer different questions — "what did this cost" and "what did +/// reuse save" — and only the first is a measurement. Keeping them in one struct with +/// separate accessors is the point: a caller that wants both gets both, and a caller +/// that wants a spend total cannot accidentally receive a netted-off number. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct AvoidedSummary { + /// How many hits were claimed. + pub hits: u64, + /// Sum of the basis figures. + pub basis_uj: u128, + /// Sum of what serving those hits cost. + pub hit_cost_uj: u128, + /// Hits where the cache did not pay for itself. + pub unprofitable_hits: u64, +} + +impl AvoidedSummary { + /// Fold one claim in. + pub fn record(&mut self, a: &AvoidedEnergy) { + self.hits += 1; + self.basis_uj += u128::from(a.basis_uj); + self.hit_cost_uj += u128::from(a.hit_cost_uj); + if !a.paid_for_itself() { + self.unprofitable_hits += 1; + } + } + + /// Net microjoules avoided across the period. Signed, for the same reason as + /// [`AvoidedEnergy::net_avoided_uj`]. + pub fn net_avoided_uj(&self) -> i128 { + self.basis_uj as i128 - self.hit_cost_uj as i128 + } + + /// A saving expressed against a spend total, in parts per million of that spend. + /// + /// Takes the spend as an argument rather than holding it, so the two totals stay + /// structurally separate and this is the only place they meet. `None` when spend + /// is zero: a ratio against nothing is not a figure, and reporting one would let + /// a system with no measured spend claim an unbounded saving rate. + pub fn saving_ppm_of(&self, spent_uj: u128) -> Option { + if spent_uj == 0 { + return None; + } + Some(self.net_avoided_uj() * 1_000_000 / spent_uj as i128) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn basis() -> ContentId { + ContentId::of_bytes(b"prior-receipt") + } + + /// The claim is only checkable because it names the execution it stands in for. + #[test] + fn an_avoided_claim_must_cite_the_receipt_it_reused() { + let a = AvoidedEnergy::against(basis(), 10_565, "HwShunt", 120, 60_000).unwrap(); + assert_eq!(a.basis_receipt, basis()); + assert_eq!(a.net_avoided_uj(), 10_565 - 120); + assert!( + a.to_json().contains(&basis().to_hex()), + "the basis receipt id must reach the wire so a verifier can resolve it" + ); + + // A basis of zero avoids nothing, so there is no claim to make. + assert_eq!( + AvoidedEnergy::against(basis(), 0, "HwShunt", 10, 1), + Err(AvoidedError::ZeroBasis) + ); + } + + /// The saving inherits the basis measurement's acquisition class: standing in + /// for an estimate yields an estimated saving, however precisely it prints. + #[test] + fn the_saving_inherits_the_basis_provenance() { + let a = AvoidedEnergy::against(basis(), 1_000, "Estimator", 10, 1).unwrap(); + assert_eq!(a.basis_provenance, "Estimator"); + assert!(a.to_json().contains("\"basis_provenance\":\"Estimator\"")); + + assert_eq!( + AvoidedEnergy::against(basis(), 1_000, " ", 10, 1), + Err(AvoidedError::NoBasisProvenance) + ); + } + + /// A cache that costs more than the work it replaces is a real outcome, and the + /// figure says so rather than clamping to "no saving". + #[test] + fn a_cache_that_does_not_pay_reports_a_negative_saving() { + let bad = AvoidedEnergy::against(basis(), 100, "HwShunt", 450, 1).unwrap(); + assert_eq!(bad.net_avoided_uj(), -350); + assert!(!bad.paid_for_itself()); + assert!(bad.to_json().contains("\"net_avoided_uj\":-350")); + + let good = AvoidedEnergy::against(basis(), 10_000, "HwShunt", 50, 1).unwrap(); + assert!(good.paid_for_itself()); + } + + /// The hazard this module exists to prevent: avoided joules are reported beside + /// spend, never merged into it. A period of pure cache hits cannot produce a + /// negative spend total, because the two never meet in one number. + #[test] + fn avoided_energy_never_becomes_spend() { + let mut sum = AvoidedSummary::default(); + for _ in 0..5 { + sum.record(&AvoidedEnergy::against(basis(), 1_000_000, "HwShunt", 100, 1).unwrap()); + } + assert_eq!(sum.hits, 5); + assert_eq!(sum.net_avoided_uj(), 5 * (1_000_000 - 100)); + + // The saving is huge relative to a small measured spend — and it is still + // expressed as a ratio TO that spend, not as a deduction FROM it. + let spent_uj = 200_u128; + let ppm = sum.saving_ppm_of(spent_uj).unwrap(); + assert!(ppm > 0); + // Nothing here yields a spend figure at all, let alone a negative one. + // `AvoidedSummary` has no `energy()` and produces no `Energy`. + + // A ratio against nothing is not a figure. + assert_eq!(sum.saving_ppm_of(0), None); + } + + /// Unprofitable hits are counted, so a cache that is net-positive overall while + /// losing on most requests is visible rather than averaged away. + #[test] + fn unprofitable_hits_are_counted_separately_from_the_net() { + let mut sum = AvoidedSummary::default(); + // One large win, three small losses. + sum.record(&AvoidedEnergy::against(basis(), 100_000, "HwShunt", 10, 1).unwrap()); + for _ in 0..3 { + sum.record(&AvoidedEnergy::against(basis(), 10, "HwShunt", 90, 1).unwrap()); + } + assert!(sum.net_avoided_uj() > 0, "net positive overall"); + assert_eq!(sum.unprofitable_hits, 3, "and losing on most hits, visibly"); + assert_eq!(sum.hits, 4); + } +} diff --git a/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs b/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs index 2b193bcfa..0d656ed08 100644 --- a/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs +++ b/jcp/jcp-rs/crates/jcp-efficiency/src/lib.rs @@ -34,6 +34,7 @@ use ed25519_dalek::SigningKey; use jcp_receipt::jcr1; use jcp_schema::{ContentId, Energy}; +pub mod avoided; pub mod training; use serde_json::{json, Value}; -- 2.54.0 (Apple Git-157)