### Typed receipt classes, and a second (soft) binding class

- [`extensions/jwp-receipts.md`](extensions/jwp-receipts.md) §2.4.3–§2.4.7: every
  receipt now belongs to one **class**. `group`/`object` are the `delivery` class, with
  their wire form unchanged; a new `claim` kind carries the others on one envelope (JCS,
  RFC 8785), one Ed25519 signing rule and a per-signer chain kept separate from the
  group chain. Classes: `energy`, `lawful-basis` (a declaration of purposes each with
  its own basis, or a decision recorded against one) and `log-inclusion` (the log's
  evidence carried verbatim — accepting the claim is not verifying the inclusion). An
  unknown class is rejected, never skipped; `delivery` is reserved to `group`/`object`.
- **`energy`** gives an object's figure a signed place for its acquisition class, which
  the object receipt has no member for
  ([`extensions/energy-measurement.md`](extensions/energy-measurement.md) §1 now says
  so, and that a group total still has none). The claim restates the figure and carries the
  class with its evidence in energy-measurement §2.7's JSON fields — `energy_provenance`,
  `energy_uncertainty`, `energy_counter_filtering`, `energy_calibration_ref`, present and
  `null` where a class has none — admitted through `crate::energy_class`: a refined
  hardware class needs its declared uncertainty above the resolution floor, a counter
  its filtering state and an instrument its calibration reference, and legacy `HwShunt`
  is refused (`energy_legacy_acquisition`, `energy_acquisition_evidence`). Its grade may
  not rank above what the class supports under
  [`extensions/oer-receipts.md`](extensions/oer-receipts.md) §2 — both refined
  hardware classes `measured`, `ModelBased` `derived`, `Estimator` `projected` — so a
  verifier that checks an accepted claim against the OER receipt it names holds one
  graded within the class the claim states. A named OER receipt
  is read, verified and its figure read by the OER module under oer-receipts' own rules
  (a vector document read under §4's profile, a figure read as §4 reads a quantity and
  compared exactly); the claim layer has no reading of OER of its own.
- §2.6 and §3.2: a **second binding class**. A publisher-signed `soft-binding`
  declaration names a registry algorithm (reverse-DNS form, aligned with the C2PA
  soft-binding algorithm list), its type, the value and an `https` resolution endpoint;
  a `binding-recovery` record states `attests: "ancestor"`. **The ancestor rule**: a
  receipt reached through a soft binding attests the ancestor, never the object in
  hand, and the transformation's energy is unaccounted rather than zero. A declaration
  counts only if signed by the publisher of an already-verified delivery receipt for its
  subject, and only once its own signature is verified — when a recovery is performed,
  and when a recorded one, or a lawful-basis decision, is checked against the
  declaration it names (`context_invalid` otherwise).
- §3.1 step 3 restates RFC 8032's own decoding rule (a key has one canonical encoding)
  and range rule (`S` < L), and fixes the two choices on which RFC 8032 verifiers
  differ: keys and `R` of small order are refused, and the equation is the cofactorless
  one. Step 1 states what JSON is `malformed` where parsers part — a byte-order mark, a
  repeated member name, an escape that leaves a surrogate unpaired, nesting deeper than
  32 — and that a number never is (`1e400`, `-0` and a 400-digit integer are
  `field_type`); `issued_at` must name a day its month has; `resolution_endpoint`'s host
  must be non-empty and its port digits. Step 2 checks exactly the type each member
  table gives, a member's type and encoding together in the table's order (a
  lawful-basis body's `record` first, a nested object's members where the table reaches
  it); what a class's rules add to a type — a non-zero figure or work count, a non-empty
  basis, an `alg` in registry form, an `https` endpoint — is step 4's, with the rule's
  own reason.
- `wai-rs --features receipt_claims` (`crate::claim_receipt`, `wai_claim_vectors`) and
  `claim-conformance/`: vectors covering every reason, run by CI in both corpus loops,
  with the feature added to the library tests. A canonical fixture computed by
  a separate implementation (`claim_verify.py`: Python JSON with UTF-16 member order,
  `cryptography` Ed25519 plus step 3's key, `R` and `S` checks written from the curve's
  definition, BLAKE3 from its specification) is pinned in `wai-rs`; that script also
  reproduces every vector's outcome, reading OER receipts with the OER reference
  verifier. `claim-conformance/differential.py` runs both verifiers on generated inputs
  the corpus does not hold and fails on any differing verdict. Both are run by hand,
  not in CI.
- OER: `py_float_repr` wrote a double half-way between two shortest decimals with the
  upper one (`75.036163330078125` as `75.03616333007813`), where the reference's `repr`
  writes the even one (`…812`). §4's number profile keeps floats out of every receipt
  body, so no content address or verdict depended on it. `str()` of a number no check
  reads did — a vector's name, which the reference and `wai-rs` (`wai_oer_vectors`,
  `wai_oer_diff`) print — so the two printed different names for such a vector. The
  function now takes the length from the shortest round-trip formatter and the digits
  from the exact one, keeping the shortest digits where the nearest decimal of that
  length does not read back (next to a power of two); a table CPython generated pins
  fourteen such doubles.
- Not done here: key discovery (every claim signature presumes the verifier holds the
  key), an authority class for who may sign which class (a lawful-basis decision is
  checked against its declaration whoever signed it: nothing supplies an operator's key
  the way a verified delivery receipt supplies a publisher's), region- or time-scoped
  soft bindings, and checking a log's receipt under its profile.

