jcp: an energy figure must say how much of the work it covers #66

Merged
dcharlot merged 1 commit from jcp/energy-coverage into main 2026-08-29 03:09:36 -04:00
Owner

Energy { measured_uj, estimated_uj, provenance }. provenance answers how the number was acquired. Nothing answered 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 neither the type nor the receipt could tell the difference.

The design

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 — the very 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 are pinned by tests; published vectors are unchanged on disk.

On blast radius — I was wrong, twice

I first declined this change on a measurement of "85 literal sites across 8 workspaces". That was wrong: a grep for Energy { had swept up mesh::Energy, proof::Energy, DeviceEnergy and AttestedEnergy — different types entirely. A second, import-filtered grep then said 2, which was also wrong in the other direction.

The compiler put the real figure at one site: a test inside jcp-schema itself.

workspace errors
joule-code, joule-ui, jouleclaw, joulehook, jouledesk, sandbox, mesh 0

Checked before and again after the receipt change, since cross-workspace breakage is the entire risk of editing this crate — and this is the crate whose last field addition broke four workspaces, three unnoticed for weeks.

Verification

240 jcp-rs tests pass · conformance passes all classes · published vectors unchanged on disk · all seven dependent workspaces check clean.

`Energy { measured_uj, estimated_uj, provenance }`. `provenance` answers **how** the number was acquired. Nothing answered **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 neither the type nor the receipt could tell the difference. ## The design `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 — the very 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 are pinned by tests; published vectors are unchanged on disk. ## On blast radius — I was wrong, twice I first **declined** this change on a measurement of *"85 literal sites across 8 workspaces"*. That was wrong: a grep for `Energy {` had swept up `mesh::Energy`, `proof::Energy`, `DeviceEnergy` and `AttestedEnergy` — different types entirely. A second, import-filtered grep then said **2**, which was also wrong in the other direction. The compiler put the real figure at **one** site: a test inside `jcp-schema` itself. | workspace | errors | |---|---| | joule-code, joule-ui, jouleclaw, joulehook, jouledesk, sandbox, mesh | **0** | Checked before *and* again after the receipt change, since cross-workspace breakage is the entire risk of editing this crate — and this is the crate whose last field addition broke four workspaces, three unnoticed for weeks. ## Verification 240 jcp-rs tests pass · conformance passes all classes · published vectors unchanged on disk · all seven dependent workspaces check clean.
`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.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!66
No description provided.