open-standards/joulecontract/conformance
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science cbf57f71a8 joulecontract: an unattested Metered tag falls to the class its evidence supports
A Metered tag whose meter attestation did not bind was always demoted to
ModelBased, but ModelBased is defined (SPEC §4.2) as a figure anchored to
a metered baseline, with that anchor sealed. A receipt with no meter and
no anchor was therefore certified ModelBased on evidence it did not
carry. An unbound reading is no reading: the class now falls to
ModelBased only where the metered-baseline anchor is sealed, else to
Unaccounted (with the `unaccounted` demotion), on the issue side and on
the sealed-evidence side alike; an unresolved anchor still demotes a
ModelBased figure to Unaccounted after that.

SPEC §4.2 and the §15 demotion table say so. The metered_unattested
negative vector, regenerated by gen_vectors, now composes to
⟨JR-3 · Unaccounted · certified⟩ with demotions metered_unattested and
unaccounted; the other 21 vectors are byte-identical. A unit test pins
both branches (no anchor → Unaccounted; anchor → ModelBased).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 22:47:45 -04:00
..
v1 joulecontract: an unattested Metered tag falls to the class its evidence supports 2026-10-11 22:47:45 -04:00
README.md

Conformance vectors — v1

Deterministic wire vectors for independent JouleContract implementations. Regenerate with:

cargo run -p joulecontract-conformance --bin gen_vectors

This pack is the normative wire form (SPEC §5.1): where prose and wire diverge, the wire rules. It was expanded at v0.8 after a clean-room implementability audit (round five, ../provenance/joulecontract-diff-v0.7-pack.md) found that the composed validation path could not be built from the published artifacts alone — every input a composed verdict needs now ships in the pack.

Reading the vectors

  • Canonical JSON (SPEC §7): UTF-8, code-point-sorted keys, no whitespace, shortest round-trip numbers with float-ness preserved (3.0 stays 3.0 — this is deliberately not RFC 8785/JCS, which would collapse it to 3). Content addresses are SHA-256 over the canonical form's bytes, written sha2-256:<hex>.
  • Expected demotions are an ordered multiset: per-discharge demotions appear once per affected discharge entry, in discharge order; receipt-level demotions appear once. A validator must reproduce the multiset; reproducing the exact order is RECOMMENDED but set-with-multiplicity equality is conformant.
  • Demotion wire tokens are the snake_case forms of the SPEC §15 names (independent_false for independent:false); undischarged serializes as an object carrying its reason.
  • claim strings are informative, not normative — match the verdict fields (demotions, max_profile, permits_claimed_profile), not the prose.
  • Timestamps are integer seconds. Obligation matching is exact (kind, text) equality; the contract's frame array declares one obligation with synthesized text "frame: " + entries joined by "+" (SPEC §5.1).
  • energy_anchor is the calibration-attestation reference required for ModelBased receipts; it is null on a Metered receipt because the meter binding plus the contract's ceiling-derivation attestation carry the anchoring there. recourse is a free-string regime identifier ("ISAE3000", "eIDAS-Art13", a bond reference); absence — not any particular value — is what serializes recourse_undeclared.
  • Verifier-key disjointness (SPEC §5.1.1) is computed against the effective generator key set; it does not require the verifier key to appear in the office key log — the log governs time coordinates (revocation windows, as-of membership), not the disjointness predicate.
Vector What a conformant implementation must do
contract.json Content-address it; derive seven obligations (two pre, two post, two inv, one frame).
receipt-conformant.json Two expected verdicts: receipt-local validation caps at JR-2 (it cannot witness the JR-3+ gates), and composed validation over the embedded sealed_context grades zero demotions, the stated max profile, and permits the claimed ⟨JR-3 · Metered · certified⟩.
receipt-jr4-conformant.json Grade JR-4: every obligation discharged twice by party-disjoint verifier entities. Quorum at JR-3 is receipt-scoped (k ≥ 2 distinct entities across the discharge log); at JR-4 it is per-obligation, expressed as multiple discharge entries per obligation (SPEC §10).
evidence-bundle.json Recompute every member address from the published bytes, recompute the bundle address (SHA-256 over the LF-joined, lexicographically sorted member address strings — SPEC §5.1.3), and match receipt.evidence. The transition record's bytes are included so receipt.transition is checkable too.
negative-*.json (11) Produce exactly the expected composed demotions and max profile against the embedded sealed_context. One case per load-bearing demotion: unbound meter, generator-key overlap (incl. the under-declared key set), entity-omission key-only independence, undischarged frame, stochastic-alone verdict, unattested generator, self-attested loop governance, undisclosed engagement, undeclared recourse, static-only frame, unterminated qualification chain.
context-*.json (3) The receipt is byte-identical to the conformant one; the demotion lives in the context: stale anchors (timestamp_stale), an open unamended breach (breach_unamended), and a key-log revocation window spanning the start anchor (verifier_revoked_window, exercising the two-timestamp Revoked event — SPEC §6.4).
malformed-missing-obligations.json Reject as malformed — an unmentioned obligation is a hole in the record, not a demotion.
malformed-inflated-allowance.json Reject as malformed — joules_allowed binds to the contract's ceiling; an inflated allowance is residual minting, not a demotion.
envelope-dsse.json Verify the DSSE envelope (Ed25519 over the PAE — formula in SPEC §7) with the published test key; recover the in-toto Statement; check _type, predicateType, and both subjects (in-toto digests use bare sha256 hex; native fields keep the sha2-256: prefix).
amendment-chain.json Verify each digest against the byte-exact files in ../archive/ — seven anchors, v0.1 through v0.7.
MANIFEST.json Verify every vector's content address before relying on it.

Not yet vectored (stated, not implied complete): composition/decomposition (SPEC §3.3), the ratchet (§5.3), re-anchor records (§3.1), sector-equivalence claims (§10), and the contract-amendment wire object (§5.2) — the reference implementation covers these in its test suite; pack vectors are future work.

The test key is not a trust root. Vectors must be reproducible, so envelope-dsse.json is signed with a published seed (0x2a × 32). It attests nothing; treat any receipt signed with it as generator_unattested everything. The pack itself is integrity-checked by MANIFEST.json and, in-repo, by the conformance crate's tests on every run.