# WAI Extension: Energy Measurement

> Status: Draft. Specifies **how a joule figure is acquired**, so that the
> `joules_micro` field the receipt profiles carry means the same thing in two
> independent implementations. It defines an acquisition class and its signed
> encoding (§2, §2.7–§2.8), an acquisition protocol with a liveness rule for the
> power reader (§3, §3.1), a quality rule (§4), and a reporting contract (§6)
> whose values are published in a signed report that names the receipt it
> describes and leaves that receipt's bytes unchanged. A receipt that carries
> none of these additions keeps exactly the bytes it had. §9 fixes, per kind of
> platform, the counter a sink reads for its decode energy, its scope, its idle
> baseline and its shortest window, and the two estimators a sink states where it
> has no counter: one for its decode, one for its display.
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.

## 1. Why an acquisition protocol

[`jwp-receipts.md`](jwp-receipts.md) defines `joules_micro` as "microjoules
consumed producing this object". That fixes the *unit* and the *scope of
attribution*. It does not fix how a number gets into the field, and without that
the field is a number-shaped hole: one implementation can write a counter
reading, another an arithmetic model, a third a constant, and every one of them
is conformant. The figures are then incomparable, and — worse — indistinguishable,
because nothing in the receipt says which kind it is.

This extension closes that. It requires two things of any receipt it governs
that carries energy: that the figure declare **how it was acquired**, and that
the measurement declare **whether it was good enough to publish**.

The contribution is not the number. It is the number *plus its acquisition
class*, and the discipline of withholding both when the measurement fails.

**Scope.** This extension governs every receipt profile WAI defines that
carries an energy figure, and the producers and verifiers of those receipts.
They include the receipts §2 lists, which carry `joules_micro` or, in the
energy binding, `microjoules`; the MoQ `WAI_RECEIPT` property
([moq-streaming-format](moq-streaming-format.md) §5); and the JWP group and
object receipts ([jwp-receipts](jwp-receipts.md) §2.4), which carry
`joules_total` and `joules_micro`. The list is not closed: a profile WAI
defines is governed as soon as it carries a figure.

It does not govern a receipt profile that WAI ingests and whose members that
profile fixes. An OER/2 receipt ([oer-receipts](oer-receipts.md)) is one. It
carries a non-zero `energy.joules_total` with no member for the class, and
cannot gain one: a member not listed for its path makes the receipt
non-canonical (§3 there). What an OER receipt carries instead is an
evidential grade, `provenance.how`, and [oer-receipts](oer-receipts.md) §2
states which grade each class supports. No rule of this extension binds an
OER receipt, or its producers and verifiers as such: not the class of §2,
and not the publication gate of §4 or the reporting contract of §6 either.
OER/2's own rules govern it, and an OER receipt with a non-zero figure and
no class breaks no rule here.

One rule reaches past the governed receipts. §2's rule for carrying a WAI
figure into a vocabulary with only three class names binds whoever carries
it, although the receipt the figure lands in is not governed.

Two receipts WAI defines carry a figure that the group signature does not
cover in its legacy form: the JWP group receipt and the JWP object receipt
([jwp-receipts](jwp-receipts.md) §2.4.1 and §2.4.2). The group receipt signs
`joules_total`, the sum of its objects' figures. The object receipt carries
its own `joules_micro`, which the group signature covers only through the
object's Merkle leaf (jwp-receipts §2.2). Each meets §2 as follows:

- **An object's figure** carries its class as an optional label (§2.8), bound
  into its Merkle leaf with the figure and the object's `origin`. Absent, the
  leaf and every signed byte are the legacy ones and the figure is unlabelled.
  Present, the leaf covers the figure, its class, the class's evidence and the
  origin, and the group signature, which covers the Merkle root, covers them
  too.
- **A group total** may carry a class of its own, signed under the labelled
  group payload (jwp-receipts §2.3) together with its coverage and the parent
  link. The class is the one the objects' classes compose to (jwp-receipts
  §2.4.1), so a holder of the group receipt alone holds a signed class for the
  total, and a holder of every object checks it (jwp-receipts §3, *The group
  total*). A group in the legacy payload carries none; a verifier holding every
  object reads its total from the objects' labels as §2.8 reads a total, and
  otherwise reads it as unlabelled.
- **A `WAI_RECEIPT` property** ([moq-streaming-format](moq-streaming-format.md)
  §5) has no signature of its own. Its class is inside a signature only where
  it equals a signed statement of the same object's figure and class: the label
  in the object's leaf, or an `energy` claim about the object. A sink reads it
  as a class only after checking that it does (moq-streaming-format §5).

An `energy` claim ([jwp-receipts](jwp-receipts.md) §2.4.5) is a receipt of its
own, signed by whoever acquired the figure. It restates an object's
`joules_micro` beside its class and the class's evidence in §2.7's JSON
fields, and meets §2. A verifier holding the claim and the object receipt
rejects the pair if the two figures differ, or, where the object's leaf is
labelled, if the two classes or their evidence differ. It is the way for a
party other than the publisher to sign a class for an object's figure.

The interactive-worlds session receipt is a profile of the group receipt that
carries the class as an optional signed label (interactive-worlds §8, and §2.8
here). It is signed under its own domain, `wai:world-receipt\x01`, not the
base group receipt's, and nothing above changes it.

Whose key signed a figure's class can be confirmed under
[jwp-receipts](jwp-receipts.md) §7: an origin the verifier has anchored lists
the key with the authority `energy/` followed by the class. That is the
origin's statement of which of its keys states figures of that class, and a key
given one class cannot state another. For the hardware classes the
confirmation holds only under an anchor the relying party pinned, never one
fetched on first use (jwp-receipts §7.5). It is not evidence about the
instrument (§2.2), and no confirmation ranks a figure above its class (§2).
jwp-receipts §7.5 recommends that a hardware-class figure be signed by a key
held by the component that signs the reading, apart from the key that signs
delivery, and has a verifier report whether the key also holds `delivery`.

## 2. The acquisition class (REQUIRED)

A receipt this extension governs (§1) that carries a non-zero energy figure
**MUST** carry an acquisition class alongside it. The one exception is the
unlabelled legacy form that §2.8 keeps for the receipt families that sealed a
figure before the class existed, the JWP object receipt and the JWP group
receipt's legacy payload among them. A `WAI_RECEIPT` property's class counts
only where §1 says it does.

| class | meaning | requires |
|---|---|---|
| `CalibratedInstrument` | Read from an instrument external to the measured device — a shunt, a power analyser, a metering socket — that holds a current calibration covering the range and conditions it was used in. | a present, non-zero measurement; a declared uncertainty (§2.3); a calibration reference (§2.5) |
| `OnChipCounter` | Read from an energy counter the measured silicon keeps about itself — RAPL, NVML, SMC, IOReport (§2.9) — or from any other instrument whose uncertainty is not traceable to a current calibration (§2.2). | a present, non-zero measurement; a declared uncertainty (§2.3); the counter's filtering state (§2.4) |
| `ModelBased` | Computed from a model calibrated on the host it describes (e.g. wall time × an active power measured on that host). A real number, honestly labelled as derived. A constant nobody calibrated there — a nominal TDP, a typical draw — is `Estimator`, however it is multiplied. | — |
| `Estimator` | A coarse constant or load estimator. | — |
| `HwShunt` *(legacy)* | Read from a hardware energy interface, sub-class unspecified, no uncertainty declared. Accepted in receipts sealed before the hardware class was refined; never emitted (§2.6). | a present, non-zero measurement |

`CalibratedInstrument` and `OnChipCounter` are refinements of `HwShunt`, the
hardware rung of the three-name vocabulary (`HwShunt` / `ModelBased` /
`Estimator`) the receipt families elsewhere in the ecosystem use. A producer
that emits a WAI figure into a vocabulary with only those three names **MUST**
map both refinements to `HwShunt`; that loses the distinction and asserts
nothing false. The vocabulary is extended, not replaced. The evidential grade
of an OER receipt ([oer-receipts](oer-receipts.md) §2) is a separate axis, and
that section states which grade each class supports.

A counter reading is **not** an estimate. RAPL, NVML and IOReport are sensor
values, and `OnChipCounter` says so (§2.9 states where this departs from other
documents in the repository). What separates the two hardware classes
from each other is what backs the figure's uncertainty (§2.2), and what
separates both from the rungs below is provenance and scope, not accuracy — see
§5.

The classes are ordered by what backs the figure's uncertainty statement,
strongest first: `CalibratedInstrument`, `OnChipCounter`, `HwShunt` (legacy),
`ModelBased`, `Estimator`. Wherever this extension says "the weaker class
applies", it means this order.

Normative:

- **An unmetered figure is not a figure of zero joules.** Where a field encodes
  unmetered as `0` — `joules_micro` in every receipt profile WAI defines, and
  the legacy fields that predate this section — that `0` is the **unmetered
  marker**: a reader **MUST** present it as unmetered and **MUST NOT** present,
  total, average or compare it as 0 J. A total over parts that include
  unmetered parts covers only the measured parts (`Partial`, energy-binding
  §4) and **MUST** say so. A field or form defined after this revision marks
  unmetered explicitly — class tag `0` beside a zero figure, as the labelled
  JWP leaf and group do (jwp-receipts §2.2, §2.3) — and never by a bare `0`
  alone. A meter that cannot measure produces no reading at all, never a
  reading of zero, and a model whose output is zero is not a measurement of
  zero either.
- An implementation **MUST NOT** emit a non-zero energy figure without a class
  in a receipt this extension governs (§1), except in the unlabelled form of
  §2.8. An unlabelled number cannot be compared to any other number.
- An implementation **MUST NOT** emit a class with a zero or absent figure. That
  claims a measurement which did not occur.
- The class **MUST** be inside the signature, so an intermediary cannot promote
  a `ModelBased` figure to a hardware class, or an `OnChipCounter` figure to a
  `CalibratedInstrument` one, in transit. The class's evidence — the declared
  uncertainty, the filtering state, the calibration reference — **MUST** be
  inside the signature with it (§2.7).
- A verifier **MUST** treat a receipt violating either coupling as invalid, not
  merely as unlabelled — with the one exception of a legacy unlabelled figure
  in a receipt family that carries the class as an optional label (§2.8) — and
  **MUST** treat a `CalibratedInstrument` or
  `OnChipCounter` class that lacks its declared uncertainty (§2.3), or a
  `CalibratedInstrument` class that lacks its calibration reference (§2.5), the
  same way.

The reference implementation enforces these rules in `verify()` rather than
documenting them, and returns *no rate at all* — never `0` — when unmetered,
because a zero reads as "this work was free", which is a stronger and false
claim than "nobody measured". The class and its evidence have one definition
(`energy_class` in `wai-rs`, mirrored byte for byte by `quantum_energy` in
`wai-quantum`), and every receipt within this extension's scope (§1) that the
reference implementation seals carries its figure in one of two ways:

- **Always present**, in the receipts defined with a class from the start: the
  energy binding and its C2PA companion assertion
  ([energy-binding](energy-binding.md)), the `wai.video.int_motion` receipt,
  the `wai.video.*` OTT receipts, and the `energy` claim
  ([jwp-receipts](jwp-receipts.md) §2.4.5).
- **As an optional label** (§2.8), in the receipt families that sealed a figure
  before the class existed and whose issued receipts must keep verifying: the
  world-session, film and reel, haptic, score, 4D-splat, audio-scene,
  ternary-reconstruction and provenance-step receipts in `wai-rs`, and every
  quantum receipt of `wai-quantum` (circuit, calibration, job, mitigation,
  decode, compile, rearrange, phasor, and the bill of materials per entry).

`wai-rs` reads and verifies JWP group and object receipts in both forms of
each, computes an object's leaf in both forms of jwp-receipts §2.2, reads a
group total from its objects, and reads a `WAI_RECEIPT` property against a
signed statement of its object (`jwp_delivery`). Neither crate writes a
`WAI_RECEIPT` property, and `wai-quantum` seals no JWP group receipt; `wai-rs`
builds them for its conformance corpus and for the objects its reference
deriving edge publishes ([deriving-edge](deriving-edge.md) §6), whose object
receipts it labels, the unmetered marker included.

### 2.1 Relationship to network-layer energy identities

The classes above are WAI's own vocabulary. An adopted IETF GREEN working-group
draft (draft-ietf-green-power-and-energy-yang, revision -04 when this section
was written) defines a graded `data-source-accuracy` identity hierarchy for device and
sub-component energy, which is the network layer's answer to the same question
this section asks: *how was this number obtained?* Its graded measured
identities are bounds on the total error of one device reading, of the form
`|actual − sensor| ≤ sensor × X %` or an absolute limit. The declared
uncertainty of §2.3 is a different kind of statement about a different
quantity, and the rules below keep the two apart.

- An implementation that also reports under a network-layer energy model
  **SHOULD** derive its acquisition class from that model's identity rather than
  assigning one independently, and **MUST** state which identity it derived from.
  Two vocabularies for one property, assigned separately, will disagree.
- A derivation **MUST NOT** improve the class. Where a network-layer identity
  maps ambiguously, the **weaker** WAI class applies. A measured identity,
  graded or not, says a sensor was read. It does not say that the sensor's
  error is traceable to a calibration, so it yields `OnChipCounter` unless the
  implementation holds the calibration reference of §2.5.
- An implementation **MUST** state how far its acquisition class lets the figure
  be leaned on. A class without that is a label, and grading provenance exists to
  put a number on it. For the two hardware classes the number is the declared
  uncertainty of §2.3. `ModelBased` and `Estimator` do not yet carry one in any
  receipt profile; that remains open.
- A graded band **MUST NOT** be declared as a figure's uncertainty. The band
  bounds one reading of the device. A WAI figure is the difference of two
  bracket means, attributed per object (§3). A pure gain error carries through
  that difference unchanged and a constant offset cancels, but readings that are
  each within a band can otherwise differ from the true marginal by far more
  than the band, relative to the difference. A band **MAY** inform the counter's
  systematic component when the declared uncertainty is evaluated (§2.3); the
  declaration remains the implementation's own evaluation of the figure.
- A declared uncertainty **MUST NOT** be used to select or assert a graded
  measured identity. No coverage factor makes the step sound, for three
  reasons:
  1. A graded band is a limit. A declared uncertainty is a standard
     uncertainty. Expanded at k = 2 it corresponds, for a normal distribution,
     to a coverage probability of about 95 %, which is not a limit.
  2. An `OnChipCounter` declaration does not cover the counter's own systematic
     error (§2.3), so it is not a statement of total error at all.
  3. The declaration is about a per-object marginal figure, and the identity is
     about a device or sub-component reading. Treating one as the other is the
     conflation the next paragraph forbids.

  A graded identity for the device's reading has to rest on a limit established
  for that reading, such as a vendor's stated accuracy for the counter or the
  maximum permissible error an instrument has been verified against over its
  range and conditions of use, under that model's own definitions. Nothing in a
  WAI receipt supplies that limit.

WAI does not adopt the network-layer model, and this section does not restate it.
The scopes differ — that model is device- and sub-component-scoped, WAI's figures
are per-object — and conflating them would breach §4's attribution rules. What is
required is that the two do not contradict each other about the same measurement.

### 2.2 Two kinds of hardware evidence

An on-chip counter and a calibrated external instrument are both hardware
readings, and both are real measurements. They are different evidence, and a
single class put them at one trust level.

- **An on-chip counter is the measured silicon reporting on itself.** Its
  reading is a sensor value. What it cannot offer is an uncertainty traceable to
  a reference: the figure's uncertainty is whatever its vendor states or the
  implementation can evaluate. Some counters also add deliberate noise to their
  output as a side-channel defence (§2.4), so the same counter is noisier over a
  short window than a long one.
- **A calibrated instrument's error has been established against a reference**
  and written into a certificate a third party can read. A deployed regime that
  signs measured energy per transaction shows the same split between signature
  and trust: in EV charging, signed meter values in the Open Charge Metering
  Format (OCMF) are accepted under German calibration law because they come
  from a conformity-assessed, legally verified measuring system — the meter
  together with the component that signs its values. The signature binds each
  value to that system so a customer can check it later. It does not make the
  value accurate; the metrological assessment does. (Legal verification against
  maximum permissible errors is not the same statement as a calibration
  certificate's uncertainty; §2.1 keeps the two apart, and §2.5 asks for the
  latter.)

A signature settles who said a number. It says nothing about how far the number
can be from the energy actually spent. Confirming whose key it is
([jwp-receipts](jwp-receipts.md) §7) settles which origin stands behind the key,
and for which class, and nothing about the reading. The declared uncertainty (§2.3) is the
statement about that, and the class says how much of it the declaration covers.
For `CalibratedInstrument` it covers the instrument's error as established by
its calibration, together with the measurement's own scatter. For
`OnChipCounter` it covers only what the implementation could evaluate, which
does not include the counter's own systematic error.

What separates the two classes is whether the uncertainty is **traceable**, not
where the sensor sits. An external meter with no current calibration **MUST**
be reported as `OnChipCounter`, the weaker class; a sense resistor read by the
device's own firmware and reported through the device's own counter is
`OnChipCounter` whatever its construction. The class therefore does not record
where the sensor sits — at the die or at the wall, with or without supply
losses — and a verifier reasoning about scope (§5) cannot learn it from the
class.

Neither class is an accuracy grade. A small declared uncertainty on an on-chip
counter says the reading was precise. How accurate it was rests on the counter,
and the class is what tells a reader that.

### 2.3 The declared uncertainty (REQUIRED for the hardware classes)

A `CalibratedInstrument` or `OnChipCounter` class **MUST** carry a declared
uncertainty of two parts:

| field | meaning |
|---|---|
| `relative_ppm` | The **standard** relative uncertainty of the figure (coverage factor k = 1, in the sense of the GUM, JCGM 100:2008), in integer parts per million of the figure. An expanded uncertainty `U` stated at coverage factor `k` — the usual form on a calibration certificate — is declared as `U / k`. |
| `window_us` | The integration window the figure was accumulated over, in integer microseconds. For the protocol of §3, the busy bracket's window. |

- Both parts **MUST** be non-zero. A relative uncertainty of zero claims a
  perfect measurement; a window of zero is not an integration. A verifier
  **MUST** treat either as an undeclared uncertainty.
- The window is not optional. Counter noise depends on it — filtered counters
  are several times noisier over 100 ms than over 1 s (§2.4) — so a relative
  error without its window cannot be compared with anything.
- An implementation converting a fractional uncertainty to `relative_ppm`
  **MUST** round up, so the declaration never states less uncertainty than was
  evaluated.
- The declaration describes the figure as sealed, and `joules_micro` is a whole
  number of microjoules. Whatever the measurement, the integer carries its own
  resolution, and the declaration **MUST** include it:
  - A figure rounded from an estimate to the nearest microjoule is within
    ±0.5 µJ of it. Taken as rectangular over that limit (GUM F.2.2.1), this adds
    a standard uncertainty `u_res = 0.5/√3 µJ` (≈ 0.289 µJ).
  - A figure read as the difference of two whole-microjoule register readings
    is within one step of the energy at each end. Two such offsets differ by a
    triangular error over (−1, +1) µJ (GUM 4.3.9), which adds
    `u_res = 1/√6 µJ` (≈ 0.408 µJ). A register whose hardware unit is coarser
    than a microjoule hides that unit from the reader, and the declaration
    **MUST** cover it separately.

  The term is fixed in microjoules, so relative to the figure it grows as the
  figure shrinks: a rounded figure of 1 µJ is uncertain by at least 29 % from
  its rounding alone, one of 10 µJ by 2.9 %, and one of 1,000 µJ by 0.029 %.
- Because of that, every declaration satisfies
  `joules_micro × relative_ppm ≥ 288 676`: the absolute standard uncertainty
  `joules_micro × relative_ppm / 10⁶ µJ` is at least `0.5/√3 µJ`, and 288 676
  is the least integer at or above `10⁶ × 0.5/√3`. A verifier **MUST** treat a
  hardware class whose declaration is below this floor as invalid. The check
  needs nothing but the receipt, and it is in integers, so every verifier
  reaches the same verdict. It is a necessary condition, not a sufficient one:
  a declaration above the floor can still understate what was not evaluated.

What the declaration covers depends on the class:

- **`CalibratedInstrument`**: the instrument's calibrated uncertainty at the
  range and conditions of use, combined with the measurement's own
  uncertainty as evaluated below.
- **`OnChipCounter`**: the components the implementation can evaluate — at
  minimum the measurement's own uncertainty as evaluated below, and any noise
  the counter's vendor states it adds at the declared window. It cannot
  include an unquantified systematic offset of the counter. An `OnChipCounter`
  declaration is therefore not a statement of the figure's total error and
  **MUST NOT** be presented as one. A reader **MUST** read it as the evaluated
  part of the figure's uncertainty, to which the counter's systematic error
  adds an amount nobody has stated. The class name is what tells a reader that.

For the bracketed marginal protocol of §3, the per-unit estimate is
`E = (mean_busy − mean_idle) × window ÷ W`, where `W` is the work the window
paid for, estimated from the completion count `C` as below. The measurement's
own relative uncertainty combines two components, and the declaration adds the
rounding of `E` to the sealed figure `F`:

```
u_power = sqrt(s_idle² / n_idle + s_busy² / n_busy) / (mean_busy − mean_idle)
u_count = u(W) / W
u_meas  = sqrt(u_power² + u_count²)                relative to E
F       = round(E)                                 the figure sealed, in µJ
u_rel   = sqrt((E · u_meas)² + (0.5/√3 µJ)²) / F   declared, relative to F
```

`u_meas` is relative to the estimate and `u_rel` to the figure a reader sees.
The measurement's part is carried across in microjoules, so a figure that
rounds down declares it relative to the smaller number. At `E` = 0.563 µJ with
`u_meas` = 1.54 %, `F` is 1 and `u_rel` is 28.9 %, not 1.54 %.

- `u_power` is the statistical (Type A) standard uncertainty of the marginal
  draw, with `s` the sample standard deviation of each bracket. It cannot be
  evaluated from fewer than two samples in either bracket, or without a
  positive marginal draw.
- `u_count` is the completion-count term. `C` is the number of reconstructions
  counted as completing inside the window, and `N` is the number that can be
  in progress at once — the worker count. The count is exact as a count; it is
  not exact as a measure of the work the window's energy paid for, because
  reconstructions part-done at an edge are invisible to it. What that error can
  be depends on how the window opened:

  | window opened | work `W` lies in | estimate `W` | `u(W)` | `u_count` |
  |---|---|---|---|---|
  | **from rest** — every worker held at a start barrier and released as the window opens | `[C, C + N)` | `C + N/2` | `N / (2√3)` | `N / (√3 · (2C + N))` |
  | **on running workers** — the workers' progress at the opening edge spread over a whole reconstruction | `(C − N, C + N)` | `C` | `N / √3` | `N / (√3 · C)` |

  From rest, no reconstruction begins before the window, so only the closing
  edge contributes: each worker is part-way through one reconstruction the
  count cannot see, and the count reads **low**, never high. On running
  workers both edges contribute, in opposite directions. In both cases, with
  only the limit known, the error is taken as rectangular over it (GUM
  §4.3.7). From rest, that is exact when the workers' progress at the closing
  edge moves together and falls anywhere in a reconstruction with equal
  likelihood — identical payloads released together do move together — and
  conservative when their progress is spread. On running workers it is
  conservative in both cases.

  A window that opens the moment the workers are started, without a barrier,
  is in neither row. When a reconstruction takes much longer than starting the
  workers, they are all near the start of one at the opening edge, so the
  count reads low by up to `N` — by about `N/2` on average — and a figure
  divided by `C` reads **high** by about `N / (2C)`: 8 % at ten workers and
  sixty completions. The symmetric running-window model does not describe
  it. The reference meters were built this way until this
  revision; their windows now open from rest.

  The term shrinks only as `C` grows, so it is large when an expensive payload
  completes only a handful of times (§3). An implementation **MUST** state
  which opening it used; a measurement report states it in its bracket's
  `opening` (§6). An implementation that removes more of this term by
  construction **MUST** state how, and **MUST** still declare whatever part it
  has not removed.
- The window is timed on a monotonic clock around the busy bracket. An
  implementation whose clock resolution is not negligible against the window
  **MUST** include it.

A figure whose `u_rel` cannot be evaluated — too few samples, no positive
marginal draw, no completions, or an estimate that rounds to zero (§4) —
**MUST NOT** be sealed under a hardware class.
`W` is the divisor of the published per-unit figure; the raw count `C` is
reported beside it (§3).

The declared uncertainty does **not** absorb the attribution bias of §4. That
bias is one-directional — a busy machine always reads low — and no symmetric
uncertainty describes it. (The completion-count bias of a window opened without
a barrier, above, runs the other way: it reads high.) The §4 ratio remains required alongside the
declaration, and a figure that fails §4 is not rescued by a large declared
uncertainty.

### 2.4 Counter filtering state (`OnChipCounter`)

Some on-chip counters filter their output as a side-channel defence. The
technical guidance published with advisory INTEL-SA-00389 (*Running Average
Power Limit Energy Reporting*, CVE-2020-8694, CVE-2020-8695) describes RAPL
energy filtering: noise added to the energy reported, typically under 2 %
standard deviation in readings at 1 s intervals, growing to 5–15 % at 100 ms,
with larger variance possible at thermal design power. Filtered energy is
reported when SGX is enabled or system software has enabled filtering (on some
implementations the processor sets it during boot to protect SGX), and TDX uses
the filtered energy information. Software
that can read model-specific registers sees it as `IA32_MISC_PACKAGE_CTLS[0]`
(`ENERGY_FILTERING_ENABLE`), supported where `IA32_ARCH_CAPABILITIES[11]` is
set; once set, that bit cannot be cleared by software.

So an `OnChipCounter` class carries the filtering state as one of:

| state | meaning |
|---|---|
| `On` | The reader determined that filtering was active. |
| `Off` | The reader positively determined that no filtering was active. |
| `Unknown` | The reader could not determine the state. |

- An implementation **MUST** report `Off` only on positive evidence. A reader
  that cannot see the control state — an unprivileged process, an interface that
  does not expose it — **MUST** report `Unknown`, never infer `Off` from
  absence.
- A verifier **MUST** read `Unknown` as possibly filtered.
- Where the state is `On` or `Unknown`, the declared uncertainty (§2.3)
  **SHOULD** include the noise the vendor states for the declared window.
- A `CalibratedInstrument` class carries no filtering state: an external
  instrument has no counter filter.

### 2.5 Calibration reference (`CalibratedInstrument`)

A `CalibratedInstrument` class **MUST** carry `calibration_ref`: a non-empty
identifier that resolves to the calibration certificate covering the
instrument as used — a URI, or the issuing laboratory's certificate number.

The reference makes the calibration claim **checkable**, not true. A verifier
holding it can obtain the certificate and confirm that it covers the
instrument, that it was current when the figure was measured, and that the
declared uncertainty is consistent with it. The receipt's signature proves who
made the claim. It says nothing about the instrument.

### 2.6 Legacy `HwShunt`

Receipts sealed before this revision carry `HwShunt`. Its meaning is fixed as
**hardware, sub-class unspecified, no declared uncertainty**.

- A verifier **MUST** accept `HwShunt` in a receipt that is otherwise valid.
  The signed encoding of §2.7 leaves every legacy receipt's signed bytes
  unchanged, so such a receipt verifies exactly as it did.
- A verifier **MUST** rank `HwShunt` below both refined hardware classes. It
  asserts a hardware reading and nothing about the reading's uncertainty.
- An implementation **MUST NOT** emit `HwShunt` in a new receipt. A producer
  that cannot say which kind of hardware evidence it has, or cannot declare an
  uncertainty for it, has not established what the refined classes require.
  A verifier cannot see when a receipt defined with a class from the start was
  sealed, so it accepts `HwShunt` there as above. The reference implementation
  meets the rule in its own producers, none of which emits `HwShunt`, and
  holds its callers to it in part: its meters cannot produce `HwShunt`, a new
  energy binding, an optional label (§2.8), the label of a JWP object's leaf or
  group, and an `energy`, `measurement` or `operating-point` claim
  ([jwp-receipts](jwp-receipts.md) §2.4.5, §2.4.8–§2.4.9) refuse it, and the `wai-rs`
  variant is deprecated, so code that names it draws a compiler warning. The
  constructors of the `wai.video.int_motion` receipt and the `wai.video.*` OTT
  receipts accept `HwShunt`, so that an issued receipt can be recreated byte
  for byte, and nothing in them refuses it. The warning reaches only code
  that names the variant. A class parsed from its label
  (`EnergyProvenance::from_label`, `from_parts`) can be `HwShunt`, as a
  verifier needs, and a caller that passes such a class to those
  constructors draws no warning.
- `HwShunt` carries no evidence fields. A receipt pairing `HwShunt` with a
  declared uncertainty, a filtering state or a calibration reference is
  ambiguous and **MUST** be rejected.

**New profiles.** The acceptance above is for receipts that could have been
sealed before the refinement, and it is closed. The profiles that keep it are
the ones listed in §2 whose figure carries a class from the start and that a
verifier cannot date: the energy binding and its C2PA companion assertion, the
`wai.video.int_motion` receipt and the `wai.video.*` OTT receipts, and the
`energy` claim; in each, an issued `HwShunt` keeps the meaning above. Every
other profile already refuses it: an optional label (§2.8), the label of a JWP
object's leaf or group, the `measurement` and `operating-point` claims, the
per-hop record of [live-metering](live-metering.md) §3.6 and the measurement a
page reports under [browser-sink](browser-sink.md) §6.

A receipt profile, record, claim, table or registration member that carries an
acquisition class and is defined after this revision (a **new profile**)
**MUST NOT** accept `HwShunt`, in any position, from any producer:

- its verifier refuses a figure classed `HwShunt` under that profile's reason
  for an invalid class, and **MUST NOT** read it as `OnChipCounter`,
  `CalibratedInstrument` or any weaker class;
- a figure that reaches a new profile from a vocabulary with only the three
  names, as `HwShunt`, enters it only where its producer states the refined
  class and its evidence again; otherwise it is carried as the unmetered
  marker. Mapping a refined class out to the three-name vocabulary (§2) is
  unchanged;
- a new profile states in its own refusals table which reason refuses
  `HwShunt`.

The reference implementation checks a new profile's class with
`energy_class::check_new_profile`, which refuses `HwShunt`
(`AcquisitionError::LegacyInNewProfile`), a class beside a zero figure, a
refined class without its evidence and a declaration below the resolution
floor of §2.3, for every profile defined since.

A package counter read through RAPL or the platform's system management unit
is an on-chip counter (§2.2): a figure read from one is `OnChipCounter`, with its
filtering state (§2.4), never `HwShunt`.

### 2.7 Signed encoding

Receipts that sign a binary payload encode the class as one tag byte, followed
by the class's evidence:

| tag | class | followed by |
|---|---|---|
| `0` | no class: the unmetered marker beside a zero figure; in a receipt family of §2.8, the slot of an unlabelled legacy figure beside a non-zero one | — |
| `1` | `HwShunt` (legacy) | — |
| `2` | `ModelBased` | — |
| `3` | `Estimator` | — |
| `4` | `OnChipCounter` | `relative_ppm` u32 BE · `window_us` u64 BE · filtering u8 (`0` Unknown, `1` Off, `2` On) |
| `5` | `CalibratedInstrument` | `relative_ppm` u32 BE · `window_us` u64 BE · reference length u32 BE · `calibration_ref` UTF-8 |

Tags 0–3 are followed by nothing, exactly as before the hardware class was
refined; that is what keeps legacy receipts verifying without re-signing.

Receipts carried as JSON carry the same evidence as named fields beside the
class label, and omit them for the classes that carry none, so a legacy body is
byte-identical to one written before this revision. The names are fixed:

| field | snake_case carriers | C2PA companion assertion (camelCase) |
|---|---|---|
| class label | `energy_provenance` | `acquisition` |
| declared uncertainty | `energy_uncertainty` `{relative_ppm, window_us}` | `uncertainty` `{relativePpm, windowUs}` |
| filtering state | `energy_counter_filtering` | `counterFiltering` |
| calibration reference | `energy_calibration_ref` | `calibrationRef` |

The snake_case carriers are the rendered `wai.video.int_motion` receipt,
every receipt carrying an optional label (§2.8), and the body of an `energy`
claim ([jwp-receipts](jwp-receipts.md) §2.4.5); in a labelled bill of
materials the fields sit inside each labelled entry. A claim has no optional
members and no legacy form, so an `energy` claim writes the fields a class does
not carry as `null` rather than omitting them, and writes an `OnChipCounter`'s
filtering state even when it is `Unknown`. Every snake_case field is
`energy_`-prefixed so that it cannot collide with a field a receipt already
renders — a quantum job receipt's `calibration_ref` names the calibration
receipt the job ran under, not a certificate. A reader **MUST** refuse evidence
fields that appear without a class label, and **MUST NOT** drop a malformed
label and read the receipt as unlabelled.

### 2.8 Optional labels on receipts that predate the class

A receipt family that sealed `joules_micro` before this extension existed has
issued receipts that must keep verifying. Such a family carries the class as
an **optional label**:

- **Absent.** The signing payload, the receipt's identity hash and its JSON are
  exactly the legacy ones. The figure is *unlabelled*: a verifier **MUST**
  accept the receipt if it is otherwise valid, and **MUST NOT** read the figure
  as any class. An unlabelled figure is not comparable with any labelled one;
  it is not upgraded to one, and in particular not to `HwShunt`.
- **Present.** The signing domain's final version byte, `0x01` in every such
  family (e.g. `wai:world-receipt\x01`), becomes `0x02`, and the class's signed
  encoding (§2.7) follows the `joules_micro` field it labels, before the fields
  that followed the figure in the legacy payload. The version byte keeps every
  labelled payload distinct from every unlabelled one, so a label can be
  neither added to nor stripped from a signed receipt; the encoding is
  self-delimiting, so the fields after it keep their legacy framing. A receipt
  whose identity hash covers its signing payload covers the label with it.
- A label **MUST NOT** be `HwShunt` — none of these receipts ever carried it —
  and **MUST NOT** sit beside a zero figure. A verifier **MUST** treat either,
  or a refined class without its evidence, as invalid.
- **New receipts.** A producer **SHOULD** label every non-zero figure it seals
  in such a family. The unlabelled form exists for receipts issued before the
  label did, and an unlabelled figure tells a reader nothing about how it was
  obtained. This is a recommendation, not a conformance requirement. No
  verifier can check it: a figure sealed unlabelled today has exactly the
  bytes of one sealed before the label existed, and a verifier accepts both
  (above). That does not set it apart from §2.6's rule on `HwShunt`, which no
  verifier can check in a receipt defined with a class from the start either.
  What sets it apart is that the reference implementation neither meets it
  in all its own producers nor holds its callers to it. Two of its producers
  seal new non-zero figures without a label (end of this section). In these
  families no constructor takes the class. `build()` and `seal()` in
  `wai-rs`, and `seal()` and the helpers that call it in `wai-quantum`, seal
  the figure unlabelled, and a label is added afterwards, by re-signing
  (below). A provenance step is labelled before its receipt is built instead:
  `ProvStep::new` makes the step unlabelled, and
  `ProvStep::with_energy_class` labels it. None of these refuses a non-zero
  figure without a label, and none of the unlabelled paths is deprecated. A
  library can hold its callers to this rule by the means §2.6 describes for
  `HwShunt`. It would need a constructor that takes the class and refuses a
  non-zero figure without one, and it would keep the unlabelled path only for
  recreating issued receipts, deprecated, so that code sealing a new
  unlabelled figure draws a compiler warning. The reference implementation
  has not done this.

Where a receipt carries several figures, each is labelled where it sits:

- A provenance receipt labels each step. A labelled step's leaf hash uses the
  `0x02` step domain with the class after the step's figure; an unlabelled step
  keeps its legacy leaf. A receipt with any labelled step signs under the `0x02`
  receipt domain and writes, after every step's figure, that step's optional
  class — `0` for an unlabelled step.
- A quantum bill of materials labels each entry the same way: under the `0x02`
  domain, every entry's figure is followed by its optional class, `0` when
  unlabelled. An entry's class restates its stage receipt's class, and a
  verifier binding the bill to its stage receipts checks that they agree.
- A JWP group receipt labels each object in that object's Merkle leaf
  ([jwp-receipts](jwp-receipts.md) §2.2). A labelled object's leaf uses the
  `0x02` leaf domain, with the figure, its class and the object's origin after
  the content hash; an unlabelled object keeps its legacy leaf. Its group may
  sign the total's own class under the `0x02` group-signing domain (§1).

A provenance or quantum bill total carries no class of its own, and neither
does a JWP group total in the legacy payload. A sum over figures of different
classes is only as strong as its weakest part, and an unmetered part is not a
zero. A reader that holds every part reads such a total as follows:

- if every part's figure is the unmetered marker, the total is unmetered;
- otherwise, if any non-zero figure is unlabelled, the total is unlabelled;
- otherwise the total reads at the weakest class among the non-zero figures, in
  §2's order, as a class name only. A total read this way carries no declared
  uncertainty and **MUST NOT** be presented as carrying one. It covers the
  whole of the work when no part is unmetered, and only the measured parts
  otherwise ([energy-binding](energy-binding.md) §4's `Whole` and `Partial`).

A reader that does not hold every part reads such a total as unlabelled. A JWP
group total in the labelled payload carries its own class instead, composed by
jwp-receipts §2.4.1's rule, which adds a declared uncertainty for a hardware
class: the parts' figure-weighted declarations, rounded up, over the longest
window — linear in the parts, so it assumes their errors fully correlated.

In the reference implementation, each `wai-rs` family has an
`energy_provenance` field and `with_energy_class()`, which re-signs with the
receipt's own key; provenance steps have `ProvStep::with_energy_class()`.
`wai-quantum`, a published crate, adds the label without changing any existing
type: `quantum_energy::Labelled<R>` wraps a sealed receipt and re-signs it
over the labelled payload, and `quantum_energy::LabelledQbom` labels a bill per
entry. A labelled quantum receipt fails the inner receipt's own `verify()` —
its signature covers the labelled payload — so a reader that does not know
about labels refuses it rather than silently dropping the class. A verifier
that accepts both forms parses with `quantum_energy::MaybeLabelled`, which
takes the form from the JSON and refuses a malformed label rather than reading
the receipt as unlabelled. The reference `wai-quantum verify` CLI and the
HTTP handler's `POST /verify` accept both forms this way. A job receipt names
its calibration by the identity of the form the calibration is in, and
`MaybeLabelled::verify_against_calibration` checks the link for either form of
each. Tests pin one unlabelled receipt of every family to the bytes it had
before the label existed.

What the reference implementation seals in these families:

- The reference meters that seal into them (`wai_meter`, `wai_world_meter`,
  `wai_quantum_meter`, `wai_prov`) label every non-zero figure they seal,
  write a measurement report (§6) for it by default, and seal a figure they
  could not measure with the unmetered marker, unlabelled.
- Two producers seal a new non-zero figure without a label, and so fall
  short of the **SHOULD** above. `world_model::frame_receipt` is handed its
  caller's figures without their class; a caller that knows the class builds
  the steps itself with `ProvStep::with_energy_class`. Two in-browser demos of
  `wai-web`, the phasor kernel and the quantum calibration and job demos, seal
  an illustrative figure the page passes in. The other in-browser quantum demos
  of `wai-web` seal the unmetered marker, since a page reads no counter, and the
  world demo seals the page's figure (wall time times a coefficient the page
  declares) labelled `Estimator`.
- A generator that recreates a committed conformance corpus byte for byte
  seals that corpus's figures unlabelled; `wai_quantum_ops_vectors gen` seals
  non-zero ones. Every committed corpus predates the label, so these
  generators recreate issued receipts, which is what the unlabelled form is
  for.
- The constructors and sealing helpers above are not counted. Each returns
  its receipt, or its provenance step, to a caller that can still label it.

### 2.9 IOReport, and where this departs from other documents

A channel of IOReport, the SoC energy interface, is classed `OnChipCounter`
when the producer reads it itself and shows it live (§3.1). Its readings are the
measured silicon's own report of its energy, and this extension classes a
figure by that provenance. The interface's channels sit in a group named
"Energy Model", and it is a private interface whose derivation is not publicly
documented. That makes the counter's systematic error unstated, which an
`OnChipCounter` declaration already excludes (§2.3). It does not make the
figure a model this implementation computed (`ModelBased`) or a constant
(`Estimator`).

The reference meters do not read such a channel. On their macOS hosts the
group's processor channels stop updating for an unprivileged reader, and the
processor power they sample is one a non-privileged reader reports from a
report it selects itself. That is a reported power (§3.1): the meters seal it
`ModelBased`, with the reader named in the measurement report, and never
`OnChipCounter`. The group's `GPU Energy` channel does advance for an
unprivileged reader; a profile that reads it itself can seal `OnChipCounter`
from it under §3.1.

Other documents in this repository class IOReport differently. This extension
does not change them; where a figure passes between the two, the weaker class
applies (§2):

| document | classes IOReport as |
|---|---|
| `jouleclaw/SPEC.md` (provenance table; realistic floors) | `ModelBased` — "vendor-provided estimate", "model-based, not measured" |
| `jouleclaw/jouleclaw-rs/crates/jouleclaw-energy/src/lib.rs` (the `ModelBased` variant's examples) and `src/apple_ioreport.rs` | `ModelBased` — a vendor model derived from frequency, voltage and utilisation |
| `jouleclaw/jouleclaw-rs/crates/jouleclaw-energy/MEASURED_ENERGY.md`, `conformance/README.md` | `ModelBased` |
| `jouleclaw/jouleclaw-rs/crates/jouleclaw-jcr1/conformance/README.md`, `vectors.json` | `ModelBased` |
| `jouleclaw/jouleclaw-rs/crates/jouleclaw-compliance/src/passport.rs` (`MeasurementMethod::IoReport`) | `Estimator`, pending an in-process reader |
| `jcp/jcp-rs/crates/jcp-energy/src/lib.rs` (`Meter::IoReport`) | `ModelBased` — a real on-die reading, carried at model grade |
| `joule-code/examples/l3-jouleclaw/src/bin/run.rs` (and its README, which describes the host as `Estimator` until IOReport is wired) | `ModelBased` |

The documents that class IOReport as hardware agree with this section at the
three-name level: `sandbox/README.md`, `proof/README.md`,
`joule-code/spec/receipt.md` (`HwShunt`), and the `eoc-meter` crate
("measured-grade"). `jcp/jcp-rs/crates/jcp-efficiency/MAPPING.md` states the
same position in prose: vendor counters are real sensor readings, whose
derivation and error bounds are undisclosed.

## 3. The reference protocol: bracketed marginal measurement

On-die energy counters report the draw of a whole package or SoC, not of one
process. A decode cannot be isolated on such an interface, so the reference
protocol measures the **marginal** draw the work adds to an otherwise-idle
machine:

1. **Idle bracket.** Sample power for a fixed count with no work running.
   Record the mean and the **standard deviation**.
2. **Saturating burst.** Run the reconstruction continuously on *N* threads,
   counting completions. Many threads are used deliberately: a single-threaded
   burst on a modern SoC often fails to lift total draw clear of idle drift.
   The threads **SHOULD** be held at a start barrier and released as the
   window opens, so the window opens from rest (§2.3).
3. **Busy bracket.** Sample power again for a fixed count while the burst runs,
   recording the wall-clock window it spans and the **standard deviation**.
4. **Attribute.** `energy = max(0, mean_busy − mean_idle) × window`, then divide
   by the work the window paid for — `C + N/2` from rest, `C` on running
   workers (§2.3).

Per-unit energy is total marginal energy ÷ the work it paid for, so the thread
count enters the figure only through the part-done reconstructions at the
window's edges, which the count cannot see. It enters the figure's uncertainty
the same way (§2.3, `u_count`). Completions **MUST** be counted per
reconstruction, not per fixed-size batch: a cheap payload runs millions of times
and an expensive one a handful, and a coarse batch collapses the count. For the
same reason an expensive payload **SHOULD** be burst over a window long enough
that its completions far outnumber the threads. With a handful of completions
the count term alone is large: ten threads and sixty completions give about
9.6 %.

An implementation **MUST** report the idle mean and standard deviation, the
busy mean and standard deviation (sample standard deviations, as §2.3 uses),
the sample counts, the window, the completion count, the thread count and how
the window opened alongside any figure it publishes. Those numbers are
what make a figure auditable, and they are what its declared uncertainty (§2.3)
is computed from; the figure alone is not auditable. Where the figure is sealed
in a receipt, they are published as §6 says.

### 3.1 A live power reader

A power reader can return values for a rail that is not being refreshed. A
frozen rail reads as zero in every sample, or as the last value the reader
holds; a rail refreshed in batches reads the same value except where one batch
lands, and one refresh can be published in two steps in adjacent samples.
Bracketed, either gives an idle bracket whose samples are all equal and a busy
bracket with a lump or a step in it: a flat idle, a positive marginal, and a
ratio (§4) that reads as clean — from no measurement of the work at all.

So a figure is sealed under a hardware class only from brackets in which the
reader's rail was **live**. A rail is live in a bracket only if:

- it advanced in two consecutive intervals;
- it advanced in at least five intervals, and in at least four of every five of
  the bracket's intervals; and
- the bracket spans at least 300 ms.

An interval is one sample of a power reader, in which the rail advanced if the
sample is strictly above zero and differs from the sample before it (the first
sample of a bracket has none before it, and advanced if it is above zero); or
one pair of successive reads of an energy counter, in which it advanced if the
second read exceeds the first, across the counter's wrap. A reader that holds
its last value repeats it, and a repeated value is not an advance, whatever the
value. Both brackets must be live. One advance is not a live rail, and neither
is one refresh published in two steps. An implementation reading a counter at
the ends of a span alone has one interval, which is never enough: it reads the
counter during the span as well.

A figure from a bracket that is not live **MUST NOT** be sealed under a hardware
class. The producer seals it with the unmetered marker, or under a weaker class
it can justify. A measurement report carries each bracket's liveness facts —
its live intervals and its longest run of them (§6.2) — so a verifier checks the
first two conditions from the report; the span is the producer's to check.

A bracket whose samples are all equal is not evidence of a quiet machine, and
never of a live rail: its samples advanced once at most. Its standard deviation
is zero, and under a hardware class a reader refuses a bracket whose `sd_uw` is
0 whatever liveness facts it carries.

**The test is run on the counter.** The rule above shows a counter live only
when the samples it is run on are the counter's: the producer reads the counter
itself and each sample is the counter's own advance over its interval. A power
that another program reports, computed from a channel the producer does not
read, gives no such evidence. Its samples can move while every channel it could
have come from is frozen, so a bracket over them that passes this rule shows the
report moving, not a counter live. A figure from a reported power **MUST NOT**
be sealed `OnChipCounter` or `CalibratedInstrument`, whatever its brackets show;
it is `ModelBased` at best, and its measurement report names the reader and
what its samples are as the method (§8; the report's `power_reader`). On the reference
macOS hosts (macOS 27, arm64) the processor channels of IOReport's
`Energy Model` group read zero at every interval for an unprivileged reader, and
the non-privileged reader the reference meters sample reports a processor power
that keeps moving, taken from another report it identifies itself (both
measured on two such hosts on 2026-10-24). Those meters therefore seal
`ModelBased` (§2.9).

## 4. Attribution quality (REQUIRED)

The marginal method is only meaningful when the burst lifts power **clearly
above the machine's own idle fluctuation**. When it does not, the method does
not degrade gracefully — it returns a confidently-formatted number that is far
too low, because an inflated idle baseline is subtracted from a busy mean that
never rose.

This is not a theoretical concern. Measuring one fixed clip with one binary,
minutes apart on the same machine:

| condition | idle | busy | marginal | reported |
|---|---|---|---|---|
| quiet | 20.45 W | 54.89 W | 34.45 W | **10,565 µJ/decode** |
| *(the quiet row predates this rule, so its ratio was not recorded)* | | | | |
| build running | ~23.9 W | 24.26 W | 0.36 W | **497 µJ/decode** |

A **21×** error, in the flattering direction, with nothing in the output to
distinguish the two.

So an implementation **MUST** compute the ratio of the marginal draw to the idle
standard deviation and report it. A measurement report reports it by carrying
the values it follows from, and every reader derives the same verdict from them
(§6.2):

| ratio | verdict |
|---|---|
| ≥ 10 | clean — publishable |
| ≥ 3 | usable for relative comparison — **not** publishable |
| ≥ 1 | **noisy — do not publish** |
| < 1 | **unusable — the machine was not idle** |

The publication bar is 10, not 3, and that is an empirical result rather than a
round number. A later reading of the same clip at a ratio of **3.8** — inside
what an earlier draft of this rule called "usable" — reported 2,469 µJ against
the 10,565 µJ above: a **4.3× disagreement from a reading the rule had passed.**
A ratio in the 3–10 band is sound for comparing two workloads measured
back-to-back on one machine, because the bias applies to both. It is not sound
for an absolute figure.

The bias has a direction. A busy machine inflates the idle baseline, which is
then subtracted from a busy mean that never rose as far, so a poor bracket
always reads **low**. An implementation reporting energy therefore errs toward
flattering itself, which is precisely why the check must be automatic rather
than left to the operator's judgement.

- An implementation **MUST NOT** seal a figure under a hardware class
  (`CalibratedInstrument`, `OnChipCounter`) when the reader's rail was not live
  (§3.1), when the ratio is below 1, or when the per-unit figure rounds to zero.
  None is a measurement of low energy; each is a failure to measure, and the
  receipt **MUST** be emitted unmetered instead.
- An implementation **MUST NOT** publish an absolute figure taken below a ratio
  of 10, and **SHOULD** warn whenever it produces one.
- A reader **MUST NOT** present a figure as publishable unless the verdict its
  measurement report gives is clean (§6.2). A reader holding no report for a
  figure cannot tell whether the figure met this bar, and **MUST NOT** say that
  it did.

Emitting an unmetered receipt is the correct outcome of a failed measurement,
not an error state. A tool that says "I did not measure this" is more useful
than one that says "this decode cost 267 µJ" when the true figure is forty times
higher.

## 5. What the figure is, and is not

- **It is machine-specific.** The same codestream on different silicon
  legitimately measures differently. Energy is a property of an execution, not
  of an artifact.
- **It is a marginal figure over an idle baseline**, so it excludes the fixed
  cost of having a machine powered on, and attributes shared platform overhead
  to the work only insofar as the work raised it.
- **It is scoped to what the interface reports** — a package or an SoC, not a
  process. Provenance and scope are the honest limitations, not sensor accuracy.
- **It depends on the build.** A debug build of the reference decoder measured
  **4.9×** a release build of the same code. An implementation **MUST** state
  the build profile alongside any published figure; a measurement report
  states it in `build` (§6).
- **It is not verifiable by re-execution.** A third party re-running the decode
  reproduces the *result* and the *work*, never the joules.

That last point is the reason the receipt profiles are shaped as they are. Where
a class's reconstruction is byte-exact and proven across architectures, a
verifier holding the artifact re-derives the output and the work unit. It cannot
re-measure the energy, and it cannot re-measure anything that describes how the
energy was obtained: the acquisition class and its evidence (§2.2–§2.5), the
bracket values of §3 and their liveness facts (§3.1), the ratio they give (§4),
the build profile, and the coverage and method a receipt states beside the
figure. Those are the measurer's statements, as the figure is, and a signature
makes them attributable, not true.

Some of them can still be checked without re-measuring anything: a declaration
against the resolution floor (§2.3), which needs nothing but the receipt; a
calibration reference against the certificate it names (§2.5); and a
measurement report's values against the figure and the declared uncertainty
they must give, and against the liveness rule (§6.2). Values that pass those
checks are consistent, not correct: a measurer who invents every value
consistently passes every one of them. Pairing a meter with a reproducible
outcome narrows what rests on the measurer to the measurement and its
description. So an implementation **SHOULD** report energy against a work unit
that a verifier can recompute from the artifact, and **MUST NOT** report it
against a work unit only the producer can compute.

## 6. Reporting contract

A published figure **MUST** be accompanied by: the acquisition class (§2), with
its declared uncertainty and window (§2.3) and its filtering state (§2.4) or
calibration reference (§2.5); the build profile (§5); the recomputable work unit
the rate is expressed against; and, for a figure obtained under the protocol of
§3, the idle mean and standard deviation, the busy mean and standard deviation,
the sample counts, each bracket's liveness facts (§3.1), the window, the
completion count, the thread count and how the window opened. The
attribution-quality ratio (§4) follows from those values (§6.2).

Where a figure is sealed in a receipt with a class, its producer **MUST**
publish these, except the work unit, which the receipt carries, as a
`measurement` claim about the figure ([jwp-receipts](jwp-receipts.md) §2.4.8).
The claim names the figure by its figure profile (§6.1), restates it with its
class, and leaves the receipt that seals it unchanged. Only the key that signed
the figure may sign the claim, so a figure that no Ed25519 key signs — an energy
binding's (§6.1) — has no `measurement` claim; carrying a binding's report in
its C2PA manifest is outside this revision. A report printed beside a receipt
but not signed does not meet this section. A record that carries the report's
values inline, under the same signature as the figure — a sink's decode table and its
segment receipt ([sink-energy](sink-energy.md) §4, §5) — meets it without a separate
claim, and §6.1 names no figure profile for them in this revision. No verifier can tell a figure whose
report was withheld from one that never had one, so this rule binds producers,
and what a verifier checks is a report it holds (§6.2).

Producers that fall short of this section, recorded rather than exempted:
the generators that recreate a committed corpus with its unlabelled legacy
figures (§2.8); `world_model::frame_receipt`, which is handed its caller's
figures without their class or their bracket; and the in-browser demos of
`wai-web` that seal a figure, illustrative (unlabelled) or estimated (labelled
`Estimator`), with no measurement behind it. A figure sealed with no class
cannot meet this section, because a `measurement` claim restates a class.

A figure published without these is not wrong so much as unusable: nothing about
it can be checked, reproduced, or compared.

### 6.1 Figure profiles

A report about a figure ([jwp-receipts](jwp-receipts.md) §2.4.8–§2.4.9) names
it by a **figure profile**, an index, and the identity of the receipt that
carries the figure. A profile is named by the domain its receipt's identity is
computed under, without the domain's final version byte. It fixes the claim's
`subject_type`, how the identity is computed, which figure an index selects,
where the figure's class is, and which key signed it.

| figure profile | receipt | `subject_type` and identity | figure; its class | index | signed by |
|---|---|---|---|---|---|
| `moq-jwp:object` | JWP object receipt (jwp-receipts §2.4.2), accepted under its group (§3) | `object`: its §2.1 content hash; the claim's `group` is the group receipt's link hash | `joules_micro`; the label in its leaf (jwp-receipts §2.2) | its leaf index | the group receipt's `signer_pubkey` |
| `moq-jwp:claim-link` | `energy` claim (jwp-receipts §2.4.5), or `reconstruct-session` claim whose `energy` is not `null` (§2.4.12) | `receipt`: its `link_hash` | `joules_micro`; its class (for a session, `energy.joules_micro` and the class `energy` writes) | `null` | its `signer_pubkey` |
| `wai:energy-binding-id` | energy binding ([energy-binding](energy-binding.md) §2) | `receipt`: `BLAKE3("wai:energy-binding-id\x01" ‖ JCS(body))` (energy-binding §4) | `microjoules`; `acquisition` | `null` | no Ed25519 key |
| `wai:video-receipt-id`, `wai:video-manifest-receipt-id`, `wai:video-ssai-receipt-id`, `wai:video-keys-receipt-id`, `wai:video-package-receipt-id`, `wai:video-channel-receipt-id` | the `wai.video.int_motion` receipt and the `wai.video.*` OTT receipts | `receipt`: its receipt hash | `joules_micro`; its class | `null` | its `signer_pubkey` |
| `wai:world-receipt-id`, `wai:film-receipt-id`, `wai:haptic-receipt-id`, `wai:score-receipt-id`, `wai:splat4d-receipt-id`, `wai:audio-receipt-id`, `wai:ternary-receipt-id` | the world-session, film and reel, haptic, score, 4D-splat, audio-scene and ternary-reconstruction receipts | `receipt`: its receipt hash, over the payload in the form it was signed (§2.8) | `joules_micro`; its optional label | `null` | its `signer_pubkey` |
| `wai:asset-receipt-id` | provenance receipt | `receipt`: its receipt hash | step `i`'s `joules_micro`; that step's optional label | step, from 0 | its `signer_pubkey` |
| `wai:quantum-receipt-id`, `wai:quantum-calibration-id`, `wai:quantum-job-id`, `wai:quantum-mitigate-id`, `wai:quantum-decode-id`, `wai:quantum-compile-id`, `wai:quantum-rearrange-id`, `wai:quantum-fabric-id`, `wai:phasor-receipt-id` | the quantum receipts | `receipt`: its receipt hash in the form it was sealed, labelled or not | `joules_micro`; its optional label | `null` | its `signer_pubkey` |
| `wai:quantum-qbom-id` | quantum bill of materials | `receipt`: its receipt hash | entry `i`'s `joules_micro`; that entry's optional label | entry, from 0 | its `signer_pubkey` |

A receipt hash here is `BLAKE3(identity domain ‖ signing payload ‖ signature)`,
as each family defines it. One content hash can be delivered in more than one
group, so a `moq-jwp:object` report also names the group, and the same object in
another group is another figure. A claim naming a profile not in this table is
refused (jwp-receipts §3.1, `figure_unknown_profile`). A receipt family that
seals a figure after this revision adds its row.

### 6.2 The measurement report

A measurement report ([jwp-receipts](jwp-receipts.md) §2.4.8) carries a figure's
§3 values as integers:

- each bracket's sample count `n`; its mean, in microwatts, rounded to the
  nearest with a half rounding up; its sample standard deviation (divisor
  `n − 1`, as §2.3 uses), in microwatts, rounded up, so a sealed dispersion never
  states less than was evaluated; and its liveness facts (§3.1): the samples in
  which the reader's rail advanced, and the longest run of consecutive ones;
- the window `T`, the busy bracket's, in microseconds rounded to the nearest:
  the `window_us` of the figure's declared uncertainty (§2.3);
- the completion count `C`, the thread count `N`, and how the window opened.

A producer **MUST** compute the figure it seals, and the least uncertainty it
declares, from those integers as below and not from the samples they summarise,
so that every verifier reproduces both exactly.

Let `M` be the busy mean less the idle mean, and let the work the window paid
for (§2.3) be `Wn / Wd`: `Wn = 2C + N` and `Wd = 2` for a window opened from
rest, `Wn = C` and `Wd = 1` on running workers. The per-unit estimate is
`E = M·T·Wd / (10⁶·Wn)` µJ, and the figure is `E` rounded to the nearest
microjoule, a half rounding up:

```
F = ⌊(2·M·T·Wd + 10⁶·Wn) / (2·10⁶·Wn)⌋
```

With `n_i`, `s_i` the idle bracket's count and standard deviation and `n_b`,
`s_b` the busy bracket's, §2.3's `u_rel` over those integers, multiplied out, is
the least integer `p` for which:

```
p² · K ≥ P,   where
K = 12 · n_i · n_b · Wn⁴ · F²
P = 12 · Wn² · T² · Wd² · (s_i² · n_b + s_b² · n_i)
  +  4 · n_i · n_b · M² · T² · Wd² · N²
  + 10¹² · n_i · n_b · Wn⁴
```

The three terms of `P` are `u_power`, `u_count` and the `0.5/√3` µJ of rounding,
each carried across in microjoules (`u_count` is `N / (√3 · Wn)` from rest and
on running workers alike). The least `p` always meets the resolution floor of
§2.3, `F · p ≥ 288 676`. Both computations are exact integer arithmetic. With
every value at most 2⁵³ − 1 (jwp-receipts §2.4.3), no product exceeds 2⁵¹² and
no floating-point operation is involved, so every verifier reaches the same `F`
and `p`.

A figure sealed under `OnChipCounter` or `CalibratedInstrument` **MUST** come
from brackets whose rail was live (§3.1), and **MUST** declare a `relative_ppm`
of at least `p` over `window_us = T`. It declares more wherever its declaration
covers more than the bracket does: an instrument's calibration (§2.3), the noise
a filtered counter's vendor states (§2.4), or a clock resolution that is not
negligible (§2.3). A figure whose `p` exceeds 2³² − 1 cannot be declared (§2.7)
and **MUST NOT** be sealed under a hardware class.

The attribution-quality ratio (§4) is `M / s_i`. Every reader derives the same
verdict from the report's values: clean when `M ≥ 10 · s_i`, usable for relative
comparison when `M ≥ 3 · s_i`, noisy when `M ≥ s_i`, and unusable otherwise. The
verdict is about the bracket's arithmetic; under a hardware class a report is
accepted only where the liveness facts also show the rail live and neither
bracket's `sd_uw` is zero, so a flat idle bracket from a frozen rail, or from a
reader holding a stale value, is refused, not read as clean.

What a verifier checks with a report is arithmetic: that the figure is the one
its values give, that the declared uncertainty is at least what they support,
and, under a hardware class, that the rail was live and the ratio is at least 1
(jwp-receipts §2.4.8). It does not check the values, which cannot be re-measured
(§5).

### 6.3 A figure divided by fabric

Work can run on more than one kind of processor, a **fabric**: `cpu`, `gpu`,
`media` (a hardware video engine) or `npu` (a neural processor). A
`measurement` report MAY carry a **fabric record** (jwp-receipts §2.4.8,
`fabrics`): the steps of the work, each with every fabric that ran it and the
backend that did, and the figure divided into one **share** per fabric that ran
a step. The figure is the sum of its shares, and its class (§2) is the class of
the whole: a share is no measurement of its own. A figure divided by fabric is
sealed under the class of its weakest share's reading, with that class's evidence
over the span; the reference meter's shares are both `OnChipCounter` readings
(§2.9), filtering `Unknown`, with a declared relative standard uncertainty of 5 %
(the counters' read granularity, not a measured spread) and the span as its window,
so its figure is an `OnChipCounter` figure. A fabric that ran a step has a share of
more than 0: 0 J is no reading of work.

Each share states how its energy was attributed to the work:

- `task`: the operating system's account of the work's own energy on that
  fabric, as it accounts a task's use of the processors, which other work on the
  host does not enter. On the reference hosts it is the kernel's per-task CPU
  energy read with `proc_pid_rusage` at `RUSAGE_INFO_V6`, `ri_energy_nj +
  ri_billed_energy − ri_serviced_energy`: the task's own energy, and the energy
  system services spent for it and the kernel billed to it, less what it spent
  servicing others. The kernel folds a thread's energy in only when the thread
  leaves its core, and a process's read of itself does not fold the thread that
  reads, so each end's read is taken only once the reading thread's own energy
  count is seen to move across a sleep; a read that does not fold is no reading.
- `rail`: the fabric's whole rail over the span, its idle and static draw over the
  span included, in which the work was the fabric's sole user: at least 99% of
  the busy time every user of the fabric accrued over it. The users are read at
  least every 200 ms through the span, each user's busy time differenced between
  consecutive reads by an identity it keeps for its life, the first read before
  the rail's start and the last after its end, so that the busy window encloses
  the rail's. A user of the work's own that vanishes, a difference that is
  negative, or a read that fails, gives no share; another user that vanishes is
  counted busy for the whole interval it vanished in, and nothing is clamped. The
  rail is read through the span, and must be live over it (§3.1). A user created
  and destroyed between two reads is not seen; that is the attribution's residual.
  The window of a `rail` share runs from the rail's republish before the span to
  its republish after it, so it can exceed the span by up to one republish
  period on each side; the figure's declared window is the span's.

A span that gives a figure divided by fabric lasts at least 1 s and five reading
intervals. No attribution divides a rail that other work shared by its users' busy
time, and a `rail` share is never a part of a rail: it is the whole rail, or there
is none.
Busy time does not apportion energy: a GPU kernel that keeps the GPU busy at a
low clock and one that keeps it busy at a high clock accrue the same time and
draw very differently, and a division by GPU time has been measured to charge
a decode three to fifteen times the GPU energy it drew alone
(`jouleclaw-energy`, `process_gpu`). Where a fabric that ran a step was shared
and its rail cannot be attributed, the span has no fabric record: the producer
meters it otherwise (§3's bracket, over the whole figure), or seals it
unmetered. So does a span that ran a step on a fabric with no live counter: on
the reference hosts (macOS 27 on arm64) the media engine and the neural processor
have none an unprivileged process can read.

A figure profile defines the vocabulary of its record's steps. A `measurement`
report about a figure whose profile defines no step vocabulary **MUST NOT** carry a
fabric record; in this revision only `wai:video-package-receipt-id` defines one
([packaging](packaging.md) §11, its steps and fabrics).

The record says where the work ran and what each fabric drew; it changes
nothing a figure's verifier checks of the figure itself (§6.2). Which fabric
ran a step changes no output of a step whose output its specification fixes
([packaging](packaging.md) §11, its steps and fabrics), so a record is a statement about energy, not
about what was produced.

## 7. Worked example

`wai.video.int_motion`, real-footage vector, 8 frames of 96×96 RGB, release
build, IOReport via a non-privileged reader:

```
work                : 221,184 subpixel-ops (8 x 96 x 96 x 3, recomputable)
cpu power  idle     : 24.95 W  (sd 0.23 W, 10 samples)
cpu power  busy     : 25.84 W  (14 samples)
marginal (busy-idle): 0.89 W
decodes             : 1,525 over 4.23 s
marginal / idle sd  : 3.8x  (usable for relative comparison — NOT publishable)
-> per decode        : 2,468.8 uJ
-> per kilo-subpixel : 11.16 uJ
acquisition class   : HwShunt
```

The run above predates the refinement of §2.2–§2.6, and its receipt says
`HwShunt`. The reference meter now emits `ModelBased` for the same reader: its
samples are a processor power the reader reports, not a counter the meter reads,
and on the reference hosts the counters that power could have come from do not
advance for an unprivileged reader (§3.1). A meter that reads a counter itself
and shows it live emits `OnChipCounter`, with its filtering state and the §2.3
uncertainty: the statistics of both brackets combined with the
completion-count term and the `0.5/√3` µJ that rounding the figure to whole
microjoules adds. It also opens its window from rest and divides by
`C + N/2`; the run above opened on freshly started workers and divided by `C`,
so its per-decode figure also carries the high-reading count bias of §2.3. None
of this can be reconstructed for that run: neither the busy bracket's standard
deviation nor the thread count was recorded, and substituting either would be
the invention this section refuses. The reference meter now also publishes
these values, with the build profile, in a measurement report (§6). The run
above printed them to its console, and its report cannot be written after the
fact.

**This example is deliberately a failing one.** It is a complete, honestly
reported bracket that this extension nonetheless forbids publishing as an
absolute figure, because 3.8 is below the bar of §4 — and the earlier reading of
the same clip on a quieter machine differed by 4.3×. It is shown rather than a
clean bracket because a clean bracket is not currently reproducible on the
authoring machine, and substituting an invented one would be the exact failure
this document exists to prevent. An implementation quoting a figure for this
vector **MUST** first obtain a bracket at ≥ 10.

The receipt seals the codestream hash, the per-frame decoded-pixel hashes, the
work figure, the energy and its class under one signature. A verifier re-derives
the codestream hash, the frame hashes and the work figure from the codestream
alone. The energy, its class and the class's evidence rest on the signer (§5); a
report about the figure lets a verifier check that they are consistent with the
bracket, and no more (§6.2).

## 8. Reproducing a figure

An implementation claiming conformance **SHOULD** publish, with any figure: the
vector decoded, the build profile and toolchain, the reader used for the power
interface, the bracket parameters of §3, and the declared uncertainty with the
evidence of §2.3–§2.5. A figure whose bracket is not
published cannot be reproduced, and an unreproducible energy number is an
assertion, not a measurement — which is the failure this extension exists to
prevent. A measurement report (§6) publishes, signed, the build profile, the
toolchain and target, the power reader and the bracket with its liveness facts,
in cleartext: the measuring machine's power levels and build environment are
visible to whoever holds the report.

## 9. Sink energy profiles

A **sink energy profile** fixes, for one kind of platform, the counter a sink reads for
the energy of its decode, what that counter covers, how its idle baseline is taken off,
and the shortest window over which a reading of it is a measurement. A sink that seals a
decode figure ([sink-energy](sink-energy.md)) names the profile it was read under, and a
reader learns the figure's scope from the profile, never from the class: the class says
what backs the uncertainty (§2.2), and nothing about where the counter sits or what it
sums. The profiles below are the ones this revision defines; a profile is named by the
term in its heading.

### 9.1 What every profile states

| item | meaning |
|---|---|
| counter | the interface and the channel read, its unit, and where it wraps |
| fabrics | the kinds of processor its reading covers — `cpu`, `gpu`, `media` (a hardware video engine), `npu` (a neural-network engine) — and what else it sums with them |
| attribution | `task` where the reading is the sink's own (below); none where the counter is every user's and the figure is §3's bracket over the whole decode |
| idle baseline | how the energy present without the decode is taken off |
| sampling interval and minimum window | the time between reads, and the shortest busy window a figure may come from |
| class | the class a reading under the profile is sealed with (§2) |

Attribution:

- **`task`**: the operating system's account of the energy the sink's own processes spent
  on the fabric, which other work on the host does not enter: on macOS `ri_energy_nj +
  ri_billed_energy − ri_serviced_energy` (§9.2).
- **`rail`**: a fabric's whole rail, attributed to one user only where that user was its
  sole user, at least 99% of the busy time every user accrued over the span, the rail and
  the busy times sampled every 200 ms through it; a span in which a user's account vanishes
  or a difference goes negative is refused. No sink profile of this revision establishes
  that for a processor; §6.4 of [sink-energy](sink-energy.md) applies the rule to a hop.
- **No attribution.** A counter every user shares, and that no rule above attributes, gives
  the sink no share of its own. The sink's figure is then §3's marginal over an idle
  bracket, over the whole decode, with §4's ratio deciding whether it is one. It is a
  figure of the whole decode and not one fabric's share, so it carries no division of the
  figure by fabric; neither does a `Partial` figure, whose fabrics are not all metered.
- A counter is never divided among its users by the time each kept it busy: busy time does
  not apportion energy (a fabric busy at a low clock and at a high clock accrues the same
  time and draws very differently). A reading that is missing or frozen is no reading, and
  is never sealed as a share of zero.

Every profile shares these rules:

- **The baseline is a bracket.** A figure is the difference of two bracket means (§3): an
  idle bracket read with the decode not running, and a busy bracket read over it. Under
  `task` attribution the idle bracket is the sink's own processes with their decode
  workers parked, so its mean is the sink's own overhead (the reads of the counter among
  it). With no attribution it is the whole machine's draw on the counter, and §4's ratio
  decides whether the decode rose clear of it.
- **Liveness and the window.** Both brackets are read at the profile's sampling interval,
  and §3.1's liveness rule applies to each. A busy window shorter than the profile's
  minimum window gives no figure.
- **Coverage.** A figure covers its scope and no more. A decode that also spends energy on
  a fabric its profile does not read is `Partial`, and names the fabrics it covers
  ([energy-binding](energy-binding.md) §4). A reader **MUST NOT** present a `Partial`
  figure as the energy of the whole decode.
- **What is not read is named.** A channel that exists but is frozen for the reader, or
  readable only with privileges the sink does not hold, is reported by name with that
  status, never as a reading of zero. Five statuses describe a counter: `live`, `frozen`
  (it exists and does not advance for this reader under §3.1), `needs-privilege` (it
  exists and this reader may not open it), `no-counter` (the hardware keeps none), and
  `not-implemented` (one exists and the sink does not read it). Only `no-counter` is the
  hardware's; the other three are the sink's to fix.
- **No legacy class.** A reading under a profile is sealed under one of §2's refined
  classes, never `HwShunt` (§2.6).
- **Filtering.** A reader that cannot see a counter's filtering control reports `Unknown`
  (§2.4). None of the profiles below gives an unprivileged reader that control.

### 9.2 macOS

**`macos-task`.** The kernel's per-process energy accounting, read with `proc_pid_rusage`
at `RUSAGE_INFO_V6`: for each process of the sink, `ri_energy_nj` plus `ri_billed_energy`
less `ri_serviced_energy`, all in nanojoules. `ri_billed_energy` is the processor energy
that system services spent on the process's behalf and that the kernel bills to it; it is
not part of `ri_energy_nj`. A decoder the sink runs as a child process is one of the
sink's processes: its counter is read while it runs and once more after it exits and
before it is reaped, since a reaped process's energy enters no counter.

| item | value |
|---|---|
| fabrics | `cpu`: the processor energy of the sink's processes and of the work billed to them |
| attribution | `task` |
| idle baseline | the sink's processes with their decode workers parked (§9.1) |
| sampling interval | 200 ms |
| minimum window | 1 s, five sampling intervals |
| class | `OnChipCounter`, filtering `Unknown` |

The counter folds a thread's energy in when the thread leaves its core, so a read is late
by at most one scheduling quantum of each running thread; over a 200 ms interval that is
inside the interval-to-interval scatter the bracket's standard deviations carry. It counts
no graphics-processor, neural-engine or media-engine energy, and no work done for the
sink outside the kernel's billing.

**`macos-gpu`.** IOReport, group `Energy Model`, channel `GPU Energy`, in nanojoules,
read without privileges.

| item | value |
|---|---|
| fabrics | `gpu`: the graphics processor's whole rail |
| attribution | none: §3's bracket over the whole decode |
| idle baseline | the machine's graphics draw before the burst (§3) |
| sampling interval | 200 ms |
| minimum window | 1 s, five sampling intervals |
| class | `OnChipCounter`, filtering `Unknown` |

The channel republishes in steps, so a bracket over it is checked for liveness like any
other (§3.1).

**What neither profile covers.** On the reference macOS sink every other channel of the
`Energy Model` group, all in millijoules, was `frozen` for an unprivileged reader at
100 ms, 500 ms and 1 s intervals: the processor clusters' `CPU Energy` channels, the
neural engine's, the video encoder's, the media scaler's, the display controllers', and
memory's. No channel of the group names the video decoder at all, so on that sink the
media engine's decode energy is `no-counter` for every reader. A hardware decode path is
therefore read under `macos-task` (the decoding process and the work billed to it) and
`macos-gpu`, and its figure is `Partial`: it does not cover the media engine. A
processor-power figure on this platform that does not come from a channel shown live
under §3.1 — one computed from cluster residency and frequency, for example — is
`ModelBased` at best, and no profile here reads one.

### 9.3 Linux

**`linux-rapl`.** The powercap interface's package zone (`/sys/class/powercap/intel-rapl:<n>`,
whose `name` is `package-<n>`): `energy_uj` in microjoules, wrapping at
`max_energy_range_uj` + 1. The same interface serves both processor vendors of the
reference sinks.

| item | value |
|---|---|
| fabrics | `cpu`: the processor package, which also sums its uncore and, where the package holds one, its integrated graphics |
| attribution | none: §3's bracket over the whole decode |
| idle baseline | the machine's package draw before the burst (§3) |
| sampling interval | 200 ms |
| minimum window | 1 s, five sampling intervals |
| class | `OnChipCounter`, filtering `Unknown` |

A sub-zone (`core`, `uncore`) lies inside its package and is never added to it; `psys`
is a separate domain. Where `energy_uj` is readable only by root or by a group the sink
is not in, the counter's status is `needs-privilege`, never `no-counter` or absent. An
interval implying more than 5 kW, or more than half the counter's range, or longer than
ten sampling intervals, is a reset or a stalled reader, not a reading, and ends the
bracket's samples there (the reader of [staged-measurement](staged-measurement.md) §8, its
paragraph 8.6).

**`linux-nvml`.** An NVIDIA device's total energy since the driver loaded,
`nvmlDeviceGetTotalEnergyConsumption`, in millijoules.

| item | value |
|---|---|
| fabrics | `gpu`: the accelerator board |
| attribution | none: §3's bracket over the whole decode |
| idle baseline | the board's draw before the burst (§3) |
| sampling interval | 1 s |
| minimum window | 5 s, five sampling intervals |
| class | `OnChipCounter`, filtering `Unknown` |

A bracket is read only once the counter has shown itself **plausible**: over the idle
bracket, its rate in no interval exceeds 1.5 times the device's enforced power limit, nor
twice the mean of the device's own power reading (`nvmlDeviceGetPowerUsage`) over the
same intervals plus 5 W. A counter that fails is `implausible`, and gives no figure. On
the reference Linux sink, read every 200 ms it rose at 20–62 W while the device's power
reading stayed near 10 W, which the rule refuses; read every second it rose at 13–21 W
against 10–11 W, which the rule admits. The rule refuses a counter that cannot be a
reading of the board; a counter it admits can still disagree with the device's own power
reading by that much, and a reader **MUST NOT** take a pass as agreement.

### 9.4 Platforms with no rail a sink can read: the estimator `estimator-cpu-time/1`

A sink whose platform gives its process no rail counter — phones today, and a browser
page — states its decode energy, where it states one at all, as an `Estimator` by this
method:

```
E = ⌊(2 · cpu_us · active_mw + 1000) / 2000⌋   µJ
```

| input | meaning |
|---|---|
| `cpu_us` | the processor time, in microseconds, the platform reports for the sink's decode over the span (its decode threads, or its process where threads are not reported) |
| `active_mw` | the declared active power of the processor cores that ran the decode, in milliwatts |
| `basis` | where `active_mw` comes from, as a reference a reader can follow; a free-text name of a device class is not a basis |
| `relative_ppm` | the uncertainty the sink states for the figure, a standard uncertainty in parts per million, non-zero |

The figure counts no hardware decoder, graphics processor or radio, and its stated
uncertainty is a statement, not an evaluation: no bracket backs it, and a reader
**MUST** present it as an estimate with that stated uncertainty. A platform that exposes
a rail counter to the sink's process is read under §9.1's rules with a profile of its
own, as an `OnChipCounter`, from the revision that registers that profile.

### 9.5 The display: the estimator `estimator-display/1`

A display's energy is not a decode's, and no sink profile reads it
([staged-measurement](staged-measurement.md) §8 keeps it out of a decode figure, in its
paragraph 8.4). Where
a sink states it, it states it apart, as an `Estimator` by this method:

```
E = ⌊(2 · on_mode_mw · nits · presented_us + 1000 · reference_nits) / (2000 · reference_nits)⌋   µJ
```

| input | meaning |
|---|---|
| `panel_class` | `backlit` (a transmissive panel lit from behind) or `emissive` (each pixel emits its own light) |
| `on_mode_mw` | the panel's declared on-mode power at its reference luminance, in milliwatts: the figure its energy label states (for example the on-mode power in standard dynamic range that Commission Delegated Regulation (EU) 2019/2013 has a display's label state) |
| `reference_nits` | the luminance, in cd/m², at which `on_mode_mw` was declared |
| `nits` | the luminance setting in effect while the content was presented, in cd/m² |
| `presented_us` | the time the content was on screen, in microseconds |
| `basis` | where `on_mode_mw` and `reference_nits` come from, as a reference a reader can follow |
| `relative_ppm` | the uncertainty the sink states, a standard uncertainty in parts per million, non-zero |

The method scales the panel's declared draw with its luminance setting and with the time
the content was shown. It does not model content: an `emissive` panel's draw follows the
picture's brightness, and the class is recorded so that a reader knows the figure's
stated uncertainty has that much more to cover. It measures nothing, and a reader
**MUST** present it as an estimate with its stated uncertainty, apart from any decode
figure, and **MUST NOT** add it to one.
