The owner's decision, applied: a vendor energy counter (RAPL, NVML, IOReport) is a real reading, and what separates one such reading from another is provenance, not accuracy. This crate gave RAPL and NVML `HwShunt` and gave the IOReport counter `ModelBased`, on the reasoning that Apple's Energy Model is a vendor model. That reasoning grades accuracy. What the counter could not establish was provenance: on macOS 27.0.1 its CPU rails are frozen for an unprivileged reader, and that is now caught by the liveness probe. A counter that has passed it is as much a reading as RAPL is. The grade. `AppleIoReportCounter` declares `HwShunt`, and stamps it on every reading through one constant, `READING_GRADE`, shared by `provenance()` and `read()`. It earns the grade by being live: the counter exists to read only where `admits()` let it through (CPU rail `Live`), and a declined counter produces no reading to carry any grade, so a frozen host still meters through the load-average estimator at `Estimator`. The rule, written once and shared (jouleclaw SPEC §1.3, §9.4; JCR-1 §2.3; the crate docs; vendor-energy PROTOCOL/README): a vendor counter's reading MAY be graded as a hardware reading only while the counter is demonstrably live for the domain the figure claims; the implementation MUST have observed the counter covering that domain advance under load (for IOReport: the CPU rail itself, in at least 5 intervals, 4 of every 5, over at least 300 ms while the probing thread burns CPU). A counter present but not live MUST NOT produce a hardware-graded figure; the implementation falls back to its estimator or reports the work unmetered, and SHOULD record why. A figure MUST NOT claim coverage of a domain its counter does not read; where the format has a coverage field, such a figure is Partial. No partial reading from the GPU rail. Where the CPU rail is frozen but `GPU Energy` still advances (the M5 Max), the counter stays declined. The GPU rail is machine-wide: every GPU client on the host moves it, display compositing included, with nothing of the metered work running. It does not cover part of the work; it covers other work, so no coverage field makes it honest. The rationale is in the module docs and SPEC §1.3. Docs and data that said otherwise: - SPEC §1.3 table and floors, §9.4; JCR-1 §2.3 and the crate README (toy vectors become measurements only off counters that were live). - JCR-1 vector 6's description said "measured on Apple IOReport". Its joules are toy values like every vector's. Only the `description` changed; it sits outside the signed core, which `verify.py` builds from `receipt` alone. Both verifiers pass 10/10 after the edit: `verify.py` (cbor2 6.1.4, cryptography 50.0.2, exit 0) and the independent `verify.go` (exit 0). `git diff` of the file is that one line; a field-by-field JSON diff finds only (6, description). The conformance README's slot 6 line follows. - vendor-energy conformance: README row 27 names `AppleIoReportCounter` and the `HwShunt` grade, with the liveness condition a filled fixture must meet; `apple-silicon-ioreport.json` is an unsigned, empty stub (`signed_by` null, every workload not run, no hash of it anywhere), so its `impl` and `provenance` are corrected in place. - MEASURED_ENERGY.md: a dated note that the 74.1 J / 25.5 W row was committed 2026-06-12, predates macOS 27, and recorded neither chip nor OS. The "reads CPU/GPU/ANE/DRAM with no sudo" claim is replaced by what macOS 27.0.1 does: the counter opens without sudo, but the CPU rails are frozen (all 645 rails on the M3 Ultra), so those hosts are estimated. - local-index / rerank tier SHOULDs, the CLI's `meter` help, meter.rs, apple.rs, energy_probe, Cargo feature comments, harness-econ docs. - l3-jouleclaw README: it claimed `HwShunt` on RAPL/NVML hosts, but the binding builds jouleclaw-energy with `ioreport` only, so off Apple Silicon the receipt is the token estimate. It now says what happens on each host. - mesh-schema's `HwShunt` definition includes vendor counters subject to liveness; mesh-meter, the mesh README and mesh-engine-gguf no longer say real silicon alone yields `HwShunt`. - The Live-branch assertions in mesh-meter, l3-jouleclaw and jouledesk-github expected `ModelBased` for IOReport; they expect `HwShunt`. jouledesk-github's fixtures that paired `apple-ioreport` with `ModelBased` now pair it with `HwShunt`, and keep a `ModelBased` case under a neutral meter name. Tests. `a_live_counters_readings_are_graded_as_hardware_readings` builds a counter on `Live` directly. That opens the real all-channel subscription, which opens here even though the gate declines this host. It reads it twice and asserts each reading is `HwShunt` over `SocTotal`. On this M5 Max (macOS 27.0.1): "an admitted IOReport counter: opens=true, 2 readings, each graded HwShunt", so the read path is covered on a frozen host. With `--features "ioreport rapl"`, `a_live_ioreport_reading_has_the_grade_of_a_rapl_reading` compares the grade with `RaplCounter`'s own. The used/declined tests assert `HwShunt` in their live branch. This host still takes the declined branch: "ioreport declined (CpuRailFrozen): 154 ms of CPU work metered by `apple-silicon` as 6200000 µJ, provenance estimator". Run on this M5 Max: jouleclaw-energy 24+1+1 (default), 76 lib + 1 + 1 (`ioreport`), 79 lib (`ioreport rapl`); jouleclaw-jcr1 50; harness-econ 14; local-index 41; rerank 37; cli 10; mesh-meter (`hw-meter-apple`) 10; mesh-engine-gguf 1; l3-jouleclaw 2 + 4 (2 ignored, need a model); jouledesk-github 12. All pass. Falsified, each mutant restored with `git checkout` and the tree confirmed clean: - `READING_GRADE` set back to `ModelBased`: the new test fails at the declared grade (left: ModelBased, right: HwShunt), 75 passed / 1 failed. - Only `read()`'s stamp set back to `ModelBased` (the declared grade left at `HwShunt`): the new test fails on the reading itself, "a live counter's reading is not graded as a hardware reading" (ModelBased, SocTotal) vs (HwShunt, SocTotal), 75 / 1. The read path is what the test pins. Co-Authored-By: Claude Opus 5.5 <[email protected]>
3.3 KiB
L3 binding — JouleClaw model tier → Joule Code cascade
A consumer binding example: how to wire JouleClaw's L3 (Model) tier into the Joule Code cascade. It is a standalone workspace, so the standard's reference library keeps no JouleClaw dependency.
A coding request that misses the cheaper tiers (L0 cache, L1 lawful, L2 embed) is
generated by a local model at L3, producing a content-addressed CodingOp and a
joules-class Receipt.
Three backends, same slot
All implement JouleClaw's jouleclaw_llm_cheap::LlmBackend, so they drop into
the same cascade stage:
EchoBackend(from JouleClaw) — deterministic, model-free; used by the unit tests so the wiring is verifiable with no model present.JouleclawRuntimeBackend(here) — runs the model in-process on JouleClaw's own from-scratch GGUF tensor runtime (jouleclaw-runtime): no external binary, no FFI. Tokenize → build the decode-step graph → compile against the registered kernels (jouleclaw-backend-reference/-apple) → autoregressive decode → detokenize, all in Rust the family wrote. This is the default backend for therunbinary.LlamaCompletionBackend(here) — drives a real on-device GGUF viallama-completion(llama.cpp). SetJOULE_CODE_L3_ENGINE=llamato use it.
Either local path: no datacenter, no network. In production the cheapest capable
backend is chosen by jouleclaw_dispatch::ModelBank's cost table.
Run it against a local model
# Default: in-process on jouleclaw-runtime (the from-scratch tensor engine).
cargo run --release --bin run -- /path/to/model.gguf "Write a Rust function that reverses a string."
# or: JOULE_CODE_L3_MODEL=/path/to/model.gguf cargo run --release --bin run
# Use the llama.cpp shell-out backend instead:
JOULE_CODE_L3_ENGINE=llama cargo run --release --bin run -- /path/to/model.gguf "..."
The run binary generates with the local model, content-addresses the result as
a CodingOp (tier L3), and puts the host energy spent during the inference on
the receipt, tagged with the provenance of whatever metered it:
- Apple Silicon whose IOReport CPU energy rail is live — JouleClaw's IOReport
counter, a live vendor energy counter: a real local measurement, tagged
HwShunt(withmeasured_ujset). - Apple Silicon whose CPU rail is not live — macOS 27.0.1 for an unprivileged
process, observed on an M5 Max and an M3 Ultra — JouleClaw's load-average
estimator, tagged
Estimator. Not a measurement. - Anywhere else, Linux and Windows included — the token-count estimate,
tagged
Estimator. This binding buildsjouleclaw-energywith theioreportfeature only, so it reads no RAPL or NVML counter even where one exists.
The printout names the meter that ran and the IOReport status, and says whether the figure was measured or estimated.
Verified live: the llama.cpp path against a local 350M GGUF, and the in-process
from-scratch runtime against ternary-bonsai-1.7b-q2_0 (qwen3, Q2_0) — real text
generated, a real op_id, the cascade closing at L3.
Tests
cargo test # EchoBackend path — no model required
# In-process from-scratch runtime against a real GGUF (opt-in):
JOULE_BONSAI=/path/to/ternary-bonsai-1.7b-q2_0.gguf \
cargo test --release --test runtime_l3 -- --ignored --nocapture