open-standards/joule-ui/conformance/README.md
Transaction Science 7609bfeaef joule-ui: a meter is an estimator until it opts in, and a charge carries its grade
Three places where joule-ui let a figure be read as more than it was.

The `Meter` trait defaulted `provenance()` to `HwShunt`. Any meter that did
not think to override it, a deterministic stub included, had its figures
sealed as hardware readings with `measured_uj` set. The default is now
`Estimator`: a meter opts in to `ModelBased` for a model and to `HwShunt` only
for readings of a hardware counter it has seen to be live. In this workspace
the only meters that are hardware readings are the two `SpanMeter` adapters
(the examples' and joule-ui-serve's), and both already return their span's
own grade, so nothing that reads hardware changes. The stubs in the
joule-ui-interaction, joule-ui-serve and joule-ui-mcp tests and the
`metered_surface` example now come out `Estimator`, as they should.

The conformance pack's `StepMeter` now declares `HwShunt` explicitly. C2 and
C4 were sealed with it standing in for a hardware counter, so they carry
`measured_uj` and exercise that path of the signed core; the explicit
override, documented as a fixture grade that attests nothing (like the
pack's steward key), is what keeps them byte-identical. SPEC §5 and §8 and
the conformance README say so.

`Authorizer::charge(action, measured_uj: u64)` handed the authorizer a bare
number, so an authorizer that keeps its own record of the debit (a JCP grant)
could not tell an estimate from a reading; the `jcp_metered` example worked
around it by reading the provenance back off the meter. `charge` now takes
`&Energy`, the exact figure the interaction is sealed with, provenance
included. Every implementor and caller in the workspace is updated, and
`jcp_metered`'s authorizer maps the charged figure's own provenance onto
`jcp_schema::Energy` instead of holding an `Rc` to the meter.

`run_interaction` turned a missing sample into `Energy::estimator(0)`. Work
that ran was sealed at 0 µJ and charged nothing, so on a host with no reading
a budget never bound, which SPEC §6 says is exactly what the budget is for.
Now, when either sample is `None`, the handler's own wall-clock time is
charged at the meter's `estimator_milliwatts()` (default
`DEFAULT_ESTIMATOR_MILLIWATTS` = 20 W, the Sandbox estimator's default),
rounded up and never 0 (`estimate_uj`), graded `Estimator`. A denial, whose
handler never ran, still records 0 µJ. The signed core and its profile tag
are unchanged.

SPEC: §5 states the default and carries the shared vendor-counter liveness
MUST verbatim (HwShunt only while the counter is demonstrably live for the
domain it claims; for IOReport the CPU rail advancing in >= 5 intervals, in
>= 4 of every 5, over >= 300 ms while the probe burns CPU; a frozen or lumped
counter never yields a hardware-graded figure; no claim of a domain the
counter does not read). The "real RAPL/IOReport" wording at §5 and "the
hardware Meter" at §9 now describe the reference meter as a hardware reading
while a vendor counter is live and its labelled estimate otherwise. §6.2
specifies the no-reading estimate and forbids recording work that ran at
0 µJ; §6.3 becomes "charge the cost, with its provenance".

Tests (joule-ui-interaction, new): `a_meter_is_an_estimator_unless_it_opts_in`,
`work_that_ran_without_a_reading_is_estimated_never_zero`,
`the_estimate_is_wall_time_at_the_declared_power_rounded_up`,
`a_budget_binds_on_a_host_with_no_reading` (a 1 J budget stops ~20 ms
interactions at 20 W within three, every debit `Estimator`), and
`charge_is_handed_the_sealed_figure_and_its_provenance`. All 31 tests across
joule-ui-interaction, -conformance (incl. the published-pack tests), -serve
and -mcp pass; the workspace builds with all targets; the three examples run
(`jcp_metered` here: five ~3 J `Estimator` interactions spend the 15 J grant
and the real JCP runtime denies the sixth). `gen_vectors` regenerated into a
scratch directory is byte-identical to `conformance/v1/` (all five files,
MANIFEST included).

Falsified, each restored and re-run green:
- default back to `HwShunt` with the fixture override removed: the vectors
  regenerate identical (under the old default they rested on the default),
  and `a_meter_is_an_estimator_unless_it_opts_in`,
  `charge_is_handed_the_sealed_figure_and_its_provenance`, joule-ui-serve's
  `firing_an_action_meters_seals_and_returns_the_next_surface` and
  joule-ui-mcp's `an_mcp_app_action_is_metered_sealed_and_attributed` fail;
- new default, override removed: `c2_interaction_is_reproduced_from_the_pack`
  and `c2_interaction_is_metered_and_allowed` fail, and the regenerated c2,
  c4 and MANIFEST differ (`"measured_uj": null, "provenance": "Estimator"`),
  so the override is what keeps the pack;
- `charge` handed `Energy::measured(uj)` regardless of grade:
  `charge_is_handed_the_sealed_figure_and_its_provenance` and
  `a_budget_binds_on_a_host_with_no_reading` fail;
- the no-reading arm back to `Energy::estimator(0)`:
  `work_that_ran_without_a_reading_is_estimated_never_zero` and
  `a_budget_binds_on_a_host_with_no_reading` fail.

Clippy --no-deps on the changed crates adds nothing; the remaining warnings
are in untouched code (joule-ui-core's tests, the conformance pack test).

Not done here: joule-ui's energy figure has no coverage field, and both
`SpanMeter` adapters grade a span whose only records are hardware GPU
records (NVML without a CPU probe) `HwShunt`, which claims the whole
interaction. §5's coverage clause forbids that; fixing it needs either a
coverage field in the signed core (a new profile tag) or a downgrade rule,
which is a decision, not a cleanup.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 20:10:14 -04:00

3 KiB
Raw Permalink Blame History

joule-ui conformance vectors — v1

Deterministic vectors for independent joule-ui implementations (SPEC §8). Regenerate with:

cargo run -p joule-ui-conformance --bin gen_vectors

A conforming implementation reproduces these from the published JSON alone — that is what makes joule-ui a vendor-neutral standard rather than one library's behaviour. Fixed Ed25519 steward key (0x17×32, published, attests nothing) and fixed meter steps make every vector reproducible.

Vector Class What a conformant implementation must do
c1-validation.json C1 — surface validation Run validate(tree, registry) over both surfaces. The valid one yields no errors; the invalid one yields the exhaustive list (every planted fault — unknown widget, missing required prop, mistyped prop, forbidden child — not just the first).
c2-interaction.json C2 — interaction metering run_interaction over a deterministic meter (step 4200 µJ) reproduces the Interaction: allow, energy 4200 µJ [HwShunt], and the content addresses of surface, inputs, and outputs. The surface id is the BLAKE3 of the canonical valid_surface JSON; inputs/outputs are content-addressed, never embedded.
c3-authorization.json C3 — authorization A zero-budget Authorizer denies the action before the handler runs: expected_handler_ran is false, the verdict is deny("budget-exceeded"), and the outputs are the genesis sentinel. A conformant runtime MUST NOT execute a denied action's handler.
c4-chain.json C4 — chain integrity verify_chain accepts the three-link chain (allow, allow, deny). Removing index 1 MUST break it at index 1: every signature verifies, seq increments from zero, and each link's prev is the prior link's content id, so no interaction can be excised, reordered, or mutated without detection.
MANIFEST.json — Verify each vector's BLAKE3 content address before relying on it.

What conformance pins, and what it does not

joule-ui pins the protocol and the receipt, not the product surfaces around them. There is no renderer vector (the renderer is the host's), no transport vector (the consumer's — MCP/SSE/Web MoQ), and no widget-library vector (product surface). The metering and capability mechanisms are recognized edges (SPEC §9): C2/C3/C4 use a deterministic stub meter and a stub budget so the vectors are reproducible, while a deployment wires the Sandbox PowerMeter (a hardware reading while a vendor counter is live, its labelled estimate otherwise) and a JCP grant. The stub meter declares HwShunt explicitly, so C2 and C4 exercise the reading path of the signed core (measured_uj present); like the steward key, that grade is a fixture and attests nothing. Outside the pack, a meter that declares no grade is Estimator (SPEC §5). What conformance guarantees is the load-bearing claim — that an interaction is validated, authorized-before-running, metered, and sealed into a tamper-evident chain, identically across implementations.