open-standards/mesh/mesh-rs/crates/mesh-core/Cargo.toml
Transaction Science 66efc6c569 mesh-meter: a declined or failed family meter is estimated, never a zero
The family contract: measured when the platform exposes it, estimated when it
doesn't, and the provenance always says which. mesh-meter's HwMeter bridges
jouleclaw-energy's detect_meter() into Mesh's Meter/Session, and seals each
span's Energy into the mesh/1 receipt.

What was asked: confirm that on a host where b9d7e945 declines the IOReport
counter, the bridge takes the no-hardware path and not a zero ModelBased
reading. It does, as long as the composite reads. detect_meter() puts the Apple
load estimator behind the declined counter, and every span says Estimator (on
both Macs: "meter=apple-silicon energy=Energy { measured_uj: None,
estimated_uj: 2000000, provenance: Estimator }").

What the check found. The bridge turned any failed read into `Sample { uj: 0,
provenance: Estimator }`, and a Session's delta is `end - start` at the end
sample's provenance:
- An unavailable counter with nothing behind it (CompositeMeter over the
  IOReport counter alone, declined) reads Err every time. Every span came out
  `estimated_uj: 0`: a zero, billed as an estimate.
- A span whose opening read failed and whose closing read succeeded was billed
  the counter's whole cumulative value at the counter's provenance (with a
  RAPL-sized history, 1,000,000,001,000 µJ tagged ModelBased in the test).
- A span whose closing read failed was billed 0.

The fix. When a read of the backend fails, the bridge takes this crate's
no-hardware path from then on: EstimatorMeter, tagged Estimator, continuing
from the backend's last good reading, so a span across the failure is billed
an estimate of its own length. It never bills zero, never the counter's
history, and never runs backwards. The fallback is sticky: once a read has
failed, the backend is not trusted again in this meter. Mixing its readings
back in would bill whichever span straddled the switch the difference between
two unrelated counters. `active_name()`/`name()` report "estimator" once it
has fallen back. The module and HwMeter docs now say the bridge can resolve to
an estimator (on Apple Silicon whose CPU rail does not advance, it does).

mesh-core, the only consumer (its demo): `--features hw-meter-apple` (and
-rapl, which the README documents, and -nvml) built the backend but did not
turn on mesh-core's own `hw-meter` feature, which is what the demo checks, so
the demo ran Mesh's 15 W EstimatorMeter and never the family meter. Each
platform feature now implies hw-meter, and a compile_error! stops a build
where one is on without it. `cargo run -p mesh-core --example demo --features
hw-meter-apple` now prints "meter: apple-silicon" and seals
"energy_provenance": "Estimator" (it printed "meter: estimator" before).

Tests (mesh-meter: 2 without features, 8 with hw-meter, 10 with hw-meter-apple,
M5 Max; the hw-meter-apple binary also on the M3 Ultra, 10 passed; mesh-core
3/3 with hw-meter-apple):
- an_unavailable_counter_takes_the_no_hardware_path_never_a_modelbased_zero: a
  backend that is not available and whose read would claim a ModelBased zero.
- the_real_ioreport_counter_when_declined_takes_the_no_hardware_path
  (hw-meter-apple, macOS): the real AppleIoReportCounter bridged alone;
  available exactly when ioreport_status() is Live, and otherwise an
  Estimator span, no measured figure, non-zero. Both machines: "ioreport=
  CpuRailFrozen available=false meter=estimator energy=Energy { measured_uj:
  None, estimated_uj: 305126 .. }" (M3 Ultra: 450329).
- on_this_host_the_bridge_is_as_honest_as_the_family_meter (hw-meter-apple):
  detect() on this host; IOReport only when Live, else Estimator.
- a_read_that_fails_mid_span_is_estimated_never_zero,
  a_span_that_opens_on_a_failed_read_is_not_billed_the_counters_history,
  a_backend_that_never_reads_is_estimated,
  once_a_read_fails_the_bridge_estimates_and_never_runs_backwards,
  a_backend_that_reads_passes_through_unchanged: a scripted backend.
These run only with hw-meter; CI's mesh-rs job builds without features.

Falsified, each mutation applied to this commit and restored with git checkout:
- FM1, main's snapshot (a failed read is `uj: 0`, Estimator): 6 tests fail,
  among them the real declined counter ("meter=none .. estimated_uj: 0") and
  the history case ("estimated_uj: 1000000001000, provenance: ModelBased").
- FM2, the backend trusted again after a failure: 2 fail, e.g. "the backend is
  not trusted again" (left: ModelBased).
- FM3, hw-meter-apple no longer implying hw-meter: mesh-core does not compile
  ("a hw-meter-* feature must enable mesh-core's `hw-meter` feature").

clippy --no-deps --tests --examples on mesh-meter and mesh-core, with no
features, hw-meter, hw-meter-apple and hw-meter-rapl: clean.

Not done here, deliberately:
- The mesh-rs CI job runs `cargo test --workspace` with no features, so none
  of the hw tests above run in CI. Adding a feature leg means editing
  .github/workflows.
- mesh-core's module doc still says Session makes the figure "measured, not
  self-reported"; with the default EstimatorMeter it is an estimate, as the
  receipt's provenance says. That predates this branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 01:55:19 -04:00

36 lines
1.3 KiB
TOML

[package]
name = "mesh-core"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
description = "Mesh — the orchestration: verify the JCP grant a request rides on, route to the cheapest-sufficient backend, meter the served span, and seal a hash-chained JCR-1 receipt whose headline figure is microjoules per accepted token (SPEC §2)."
[dependencies]
mesh-schema = { workspace = true }
mesh-meter = { workspace = true }
mesh-receipt = { workspace = true }
mesh-engine = { workspace = true }
mesh-router = { workspace = true }
jcp-schema = { workspace = true }
jcp-grant = { workspace = true }
ed25519-dalek = { workspace = true }
thiserror = { workspace = true }
[dev-dependencies]
hex = { workspace = true }
serde_json = { workspace = true }
[features]
default = []
# Forwarded to mesh-meter: use the family's hardware-backed meter. Each platform
# feature implies `hw-meter`, which is what the demo checks: without it
# `--features hw-meter-rapl` built the backend and then used the estimator.
hw-meter = ["mesh-meter/hw-meter"]
hw-meter-rapl = ["hw-meter", "mesh-meter/hw-meter-rapl"]
hw-meter-nvml = ["hw-meter", "mesh-meter/hw-meter-nvml"]
hw-meter-apple = ["hw-meter", "mesh-meter/hw-meter-apple"]
[lints]
workspace = true