jcp: an energy figure must say how much of the work it covers (re-land) #67

Merged
dcharlot merged 1 commit from jcp/energy-coverage-relanded into main 2026-08-29 03:11:37 -04:00

2026-08-29

David Charlot
jcp: an energy figure must say how much of the work it covers
`Energy { measured_uj, estimated_uj, provenance }`. `provenance` answers *how*
the number was acquired and nothing answers *how much of the task it accounts
for* — two different questions, routinely conflated to the figure's advantage.

The concrete case: on a host where the accelerator exposes an energy counter but
the CPU does not, a caller reads the accelerator and `Energy::measured()` stamps
`HwShunt` on a figure presented as the task's energy. The CPU's share is not
zero; it is unmeasured and silently missing. The reading is honest about its
sensor and dishonest about its scope, and nothing in the type or the receipt
could tell the difference.

`Coverage { Whole, Partial }` sits beside provenance rather than inside it,
because the axes really are independent: a partial reading is still a genuine
sensor reading, and `check_provenance` is untouched. `Energy::measured_partial`
exists so the honest case is as easy to express as the flattering one — without
it, a caller holding a partial reading reaches for `measured()` and the figure
claims the whole task by default.

The distinction is carried into the sealed receipt, not just the type. Had it
stopped at the type, a partial reading would reach a verifier looking exactly
like a whole one, which is the failure the field was added to prevent.

`Whole` is the default and is omitted from the wire, so every receipt sealed
before this field canonicalizes to the same bytes, and a legacy payload with no
`coverage` key parses as `Whole` — both pinned by tests, and the published
vectors are unchanged on disk.

On blast radius: I first declined this change on a measurement of "85 literal
sites across 8 workspaces", which was wrong — a grep for `Energy {` had swept up
`mesh::Energy`, `proof::Energy`, `DeviceEnergy` and `AttestedEnergy`, which are
different types. The compiler put the real figure at **one** site, a test inside
`jcp-schema` itself. Every one of the eight path-depending workspaces compiles
untouched, checked before and again after the receipt change, since that is the
whole risk of editing this crate.

Verified: 240 jcp-rs tests pass, conformance passes all classes, published
vectors unchanged, and joule-code / joule-ui / jouleclaw / joulehook / jouledesk
/ sandbox / mesh all check clean with zero errors.
2026-08-29 03:10:53 -04:00