A price book of version 2 prices each operation of an endpoint in its own entry, and a job's candidates are the entries of its operation, so an encode and a decode are estimated, capped and ranked at their own prices. An entry states its measured failures over a basis (hops, period), and a candidate carries its expected cost and expected time to first byte per successful job (the list figure plus failed/(hops - failed) failures' worth), which the price, ttfb and targets keys read first: the order that minimises the expected spend over the attempts. A basis under 100 hops is too thin; thin or null failures are unknown, their expected figures null and ranked by the existing rule for null. The job pays the list price: the limits read it and a line bills the billed attempt. A record of version 2 states the served hop's outputs, and its OER/2 receipt's subject.output must be their commitment (oer-receipts §3 Output: the content hash of one, the jwp-receipts §2.2 root over several), or receipt_output_mismatch. Version 1 documents and records verify exactly as before; the corpus keeps every version-1 vector byte for byte but the unknown-version control (now version 3) and adds 36 of version 2. route_record_verify.py reads version 2 from the text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
34 KiB
WAI Extension: OER Receipts (outcome and energy)
Status: Draft. Ingests OER/2 draft 0.1, with the departures in §9 and the members minor version 1 adds (§3.1): the unmetered marker and the acquisition class of a receipt's figure. An OER receipt records what one unit of work was, what it cost, how that cost is graded, and what it rests on — canonicalised, content-addressed, hash-chained and Ed25519-signed, so a third party can check every figure without trusting the producer. Conformance is reason-set-equivalence: for every published vector, a verifier reports exactly the reasons the vector declares. Verifiers: the reference,
oer-conformance/oer2_verify.py, andwai-rs(crate::oer, theoerfeature); corpus:oer-conformance/. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.
1. Scope and model
energy-measurement.md fixes how a joule figure is
acquired — the acquisition class. It does not say how much weight the figure can
bear. An OER receipt adds that: every figure carries an evidential grade, and
the receipt as a whole is canonical, content-addressed and chained, so the grade
cannot be quietly upgraded after the fact. A receipt of minor version 0 carries the
grade and no acquisition class; one of minor version 1 may carry both (§3.1). The two
are different axes (§2).
A receipt describes one unit of work of a declared kind, one of the strings
decision, task, reconstruction, calibration, job, compile, decode,
mitigate, rearrange, anchor, or bundle (of other receipts). Receipts form a
chain; the chain, the records its decisions relied on, and a ledger identity that
ties the chain to a meter are verified together.
Three independent implementations check the same corpus: the reference verifier
(oer-conformance/oer2_verify.py), the portable C kernel that produces receipts
(oer-conformance/settle/), and wai-rs. They share no canonicalisation code.
2. The evidential grade (REQUIRED)
provenance.how MUST be one of four grades, weakest first:
| grade | meaning |
|---|---|
projected |
an estimate awaiting a measurement |
simulated |
produced by emulation or a model run, not by the system itself |
derived |
follows from measured or published figures by stated arithmetic |
measured |
read from an instrument, a datasheet, or a published measurement |
A figure's grade is its evidential standing; its acquisition class
(energy-measurement.md §2: CalibratedInstrument,
OnChipCounter, ModelBased, Estimator, and the legacy HwShunt) is how it
was obtained. They are different axes and a receipt SHOULD carry both where
it carries an acquisition class at all. A receipt of minor version 0 has no member
for the class, and cannot gain one without becoming non-canonical (§3); one of minor
version 1 may state it as energy.class (§3.1). Their descriptive members name
none. provenance.device is text; the corpus writes hardware,
emulation and simulation. energy.method is text too; the corpus writes
hardware_meter and model_estimated. In particular, neither hardware nor
hardware_meter is HwShunt or either refined hardware class.
energy-measurement.md governs the receipt profiles
WAI defines (§1 there), not an OER/2 receipt. No rule of that extension binds an
OER receipt or its producers and verifiers: an OER receipt that carries a
non-zero energy.joules_total and no class breaks none of them, and that
extension's publication gate (§4 there) and reporting contract (§6 there) do not
apply to it either. Its evidential axis is the grade, under this document's
rules.
The two axes correspond in one direction:
| acquisition class | supports the grade |
|---|---|
CalibratedInstrument |
measured |
OnChipCounter |
measured |
HwShunt (legacy) |
measured: a figure sealed before the hardware class was refined, or a refined hardware figure mapped into the three-name vocabulary (below) |
ModelBased |
derived, by energy-measurement.md's own definition |
Estimator |
projected |
- Both refined hardware classes are readings of an instrument, so both support
measured. The grade does not carry what separates them: whether the figure's uncertainty is traceable to a calibration (energy-measurement.md§2.2). The class and its declared uncertainty (§2.3 there) say that;measureddoes not. - Legacy
HwShuntis what WAI receipts sealed before the refinement say. It ranks below both refined classes, andenergy-measurement.md§2.6 forbids it in a new WAI receipt, so a hardware figure a conforming WAI receipt seals today namesOnChipCounterorCalibratedInstrument. Mapped into a vocabulary that has onlyHwShunt/ModelBased/Estimator, both refined classes becomeHwShunt(§2 there); the grade does not change, so aHwShuntfigure produced that way supportsmeasuredtoo. A receipt that records no class says, by a figure gradedmeasured, nothing of which hardware class, if any, is behind it. - The correspondence does not run the other way:
measuredalso covers a datasheet or a published figure, which no acquisition class names, andsimulatedhas no acquisition-class counterpart at all.
A producer MUST NOT grade measured a figure whose acquisition class is
Estimator. A receipt of minor version 0 carries no acquisition-class member (§3),
so no reason in §7 checks this there, and it binds the producer; in a receipt of minor
version 1 that states its class, class_grade_mismatch checks it (§3.1).
provenance.weakest_input names the lowest-graded input the figure rests on, so a
measured total built on a projected constant is visible as such.
The energy claim class of jwp-receipts.md §2.4.5 carries both
axes for a delivery receipt's figure: the acquisition class with its evidence, and a
grade, which it rejects when it ranks above what the class supports under the
correspondence above. It may name the OER receipt that records the figure. A verifier
holding that receipt then checks that it verifies under this document, is about the
same object (its subject.output is the object's jwp-receipts §2.1 content hash, as
the claim writes it), and states the same grade and, to the microjoule, the same
figure, read as §4 reads a quantity. That is where the producer rule above becomes
checkable for a receipt that states no class, against the class the claim's signer
states: a verifier that checks an accepted energy claim against the OER receipt it
names holds a receipt graded no higher than that class supports. It changes nothing
about the OER receipt itself. A receipt that states the unmetered marker (§3.1) states
no figure, so no claim's figure is that receipt's.
3. Receipt members
Members are fixed per path, and only these members may appear:
| path | members, in canonical order |
|---|---|
| (root) | oer, receipt_id, kind, capability, intent, subject, work, energy, provenance, settlement, floor, efficiency, bundle, chain, hash_alg, sig_alg |
subject |
inputs, output |
work |
status, outcome, latency_s, proficiency |
energy |
joules_total, boundary, breakdown, method, temperature_k |
energy.breakdown |
compute_j, movement_j, idle_alloc_j, shared_alloc_j, upstream_j |
provenance |
how, device, weakest_input, hardware_profile, corpus |
provenance.weakest_input |
how, input |
settlement |
question_key, served_from_record, record_id, record_hash, reuse_count, derivation_j, valid_until |
floor |
bits_committed, bits_delivered, floor_j, decades_above_floor |
efficiency |
completed_units, wasted_units, joules_per_completed, waste_fraction, value_per_joule |
bundle |
stages, provenance_root |
bundle.stages[] |
kind, receipt_hash, joules |
chain |
prev_hash, receipt_hash, signer, signature, anchor |
chain.anchor |
path, delivered_at, root |
An object at any other path orders its members by key. A member not listed for its path makes the receipt non-canonical.
oer is the profile version, a string ("2.0"); its major version is the part
before the first .. A verifier MUST reject a major version other than 2, and
an oer that is not a string, before any check but whether the receipt is an object
(§7) — a version written as a JSON number would have a major that depends on how
each parser rounds it. energy.boundary MUST be stated: a joule figure without
its measurement boundary cannot be compared with any other.
Capability. capability names what the receipt is about, in the receipt's own
namespace: a decision receipt names its decision class under oer.decision.*
(oer.decision.ev_charging_authorization), and bundles and anchors use oer.bundle.*
and oer.anchor.*. A job receipt names the operation its unit of work ran under oer.job.*:
oer.job.<operation>, where <operation> is the id of an operation of WAI's
operation catalogue (wai/catalogue/ops.json, noun.verb), so a hop that encoded
names oer.job.media.encode and one that packaged oer.job.package.build. A record
that names a job's operation, such as a route record
(route-record §6), holds each hop's receipt to that name. These are
not WAI payload capabilities, which name formats a sink reconstructs
(SPEC.md §5), and a receipt MUST NOT name them in the wai.
namespace.
Output. A job receipt names the objects its unit of work delivered in subject.output,
by their commitment, written as 64 lowercase hexadecimal digits:
- one object, by its content hash (jwp-receipts §2.1), the form an
energyclaim holds an OER receipt to (§2, jwp-receipts §2.4.5); - several, delivered in an order, by the Merkle root jwp-receipts §2.2 builds over their content hashes in that order, every leaf of the first, unlabelled form: the root a group of exactly those objects is signed over when none carries a class label. The objects are distinct: the tree pads an odd level with a copy of its last node, so a list that repeats an object could share a root with a shorter one, and distinct objects give each list its own root.
The root of one object is not its content hash, and a commitment of several never names one of them alone: one value names one list. No reason of §7 checks the commitment, since a receipt on its own does not show what it delivered; a record that states what a job delivered holds its hop's receipt to it, as a route record of version 2 does (route-record §6).
Status, escalation and abstention. work.status is 1 for a unit of work that
completed and -1 for one that was wasted; a bundle's efficiency counts them as
completed_units and wasted_units. Two outcomes are easy to confuse:
escalate— the question was routed to a higher authority and answered there. It is a verdict:subject.outputcarries the answer and the receipt may store a record that later receipts are served from. This unit did not derive the answer, and says so.abstain— no answer. The receipt makes no verdict claim:subject.outputis empty, and it stores no record (settlement.served_from_recordfalse,record_idandrecord_hashempty), so nothing can ever be served from it as settled. A verifier reportsabstention_settledfor an abstention that carries any of them. Empty and false here mean absent,null,false,0,"",[]or{}; any other value is a claim.
A producer MUST receipt a decision it did not earn — one closed by a stand-in, a
default or a guess — as an abstention, never as a verdict. An unsettled answer is a
first-class result. An abstention is completed work (status 1), so
efficiency.joules_per_completed counts it; a figure per settled decision
MUST NOT.
3.1 Minor version 1: the unmetered marker and the acquisition class
A receipt whose oer is exactly "2.1" is of minor version 1; any other "2.x"
is read as minor version 0, under §3's members alone. A receipt of minor version 1 may
carry two things a receipt of minor version 0 cannot, and nothing else:
| path | members, in canonical order |
|---|---|
energy |
joules_total, boundary, breakdown, method, temperature_k, class |
energy.class |
label, relative_ppm, window_us, filtering, calibration_ref |
The other paths keep §3's members. Under minor version 0 an energy.class is a member
not listed for its path (canonical_failure) and a null joules_total is not a
quantity (malformed), as before this revision, so no receipt of minor version 0 reads
differently. A verifier that knows only minor version 0 refuses a receipt that uses
either, rather than misreading it.
The unmetered marker. An energy.joules_total of null states that nobody measured
the unit of work's energy. It is not a figure of zero joules, and a verifier MUST
report the receipt as unmetered, never as a figure (energy-measurement §2). A receipt
that states it MUST write every energy.breakdown term as null, MUST be
graded projected (no figure stands behind it to be measured or derived), MUST NOT
carry energy.class, and MUST NOT be a bundle, whose figure is its stages' sum
(§6); a receipt that breaks any of these is unmetered_invalid. Its energy.boundary
is still stated: it names what would have been measured. Nothing sums to the marker, so
the sum check (§6) does not run for it, and a ledger identity over a chain that holds
one does not hold (§6, ledger_mismatch).
The acquisition class. energy.class states how joules_total was acquired, as
energy-measurement §2 names the classes. label is one of
CalibratedInstrument, OnChipCounter, ModelBased and Estimator; the legacy
HwShunt is not admitted (energy-measurement §2.6, New profiles). Each label carries
exactly its evidence and no other member:
label |
members |
|---|---|
OnChipCounter |
label, relative_ppm (an integer 1 to 2³² − 1: the declared standard relative uncertainty in parts per million, energy-measurement §2.3), window_us (an integer 1 to 2⁵³ − 1: the integration window in microseconds), filtering ("Unknown", "Off" or "On": whether the counter filtered its output, energy-measurement §2.4) |
CalibratedInstrument |
label, relative_ppm, window_us, calibration_ref (a non-empty string: the calibration certificate's identifier) |
ModelBased, Estimator |
label |
A class that is not an object of these members, each of its form, is class_invalid.
A valid class MUST support exactly the receipt's grade, under §2's correspondence
(CalibratedInstrument and OnChipCounter measured, ModelBased derived,
Estimator projected); a receipt graded otherwise is class_grade_mismatch, checked
when the grade is one of §2's (an unknown grade is grade_unknown alone). So the
producer rule of §2 is a reason here: a counter reading graded derived, an estimate
graded measured and a simulated figure with any class are each refused. A receipt of
minor version 1 may omit the class, and then reads as one of minor version 0 does.
4. Canonical form
The canonical body is the receipt without chain and receipt_id, with every
object's members in the order of §3, serialised as JSON with , and : as
separators and no other whitespace, and with non-ASCII characters written
unescaped (UTF-8). Within strings, " \ and the controls \b \f \n \r \t take
their short escapes and every other character below U+0020 is written \u00XX in
lower-case hex.
Numbers. A receipt body carries two kinds of number and no others. Integers are
written in plain decimal, with no fraction or exponent, and lie within ±(2⁵³ − 1):
the I-JSON range (RFC 7493 §2.2), which any reader of IEEE 754 binary64 or wider
reads exactly. Every non-integer quantity — every joule figure, temperature and ratio
— is a decimal string ("14.960786694120"). A number with a fraction or an
exponent (1.0, 1e2), negative zero (-0), and an integer outside that range
MUST NOT appear in a body, and a verifier MUST report malformed for one.
The canonical form therefore never prints a float. That matters because parsers
disagree on exactly these shapes — one reads -0 and 2⁶⁴ as exact integers, another
as floats — and two verifiers that hashed them would compute two receipt ids for one
receipt. The rule is about how a number is written: a verifier whose parser reads
1.0 and 1 as the same value needs a reader that sees the text. Records (§6) are
not receipt bodies and are unaffected.
The document. A vector document is read before any receipt in it is verified, and one that cannot be read under this profile is refused whole:
- a number whose nearest binary64, rounding half to even, is infinite — magnitude 2¹⁰²⁴ − 2⁹⁷⁰ or more, in any spelling. A reader MUST round correctly: one that does not refuses finite numbers just below the midpoint, or reads ones past it;
NaN,Infinityor-Infinity, which are not JSON;- a string holding an unpaired surrogate, which is not Unicode (RFC 7493 §2.1);
- a member name repeated within one object (RFC 7493 §2.3) — readers disagree on which value wins, so a signed receipt could carry a second, hidden figure;
- nesting deeper than 64 arrays and objects, the document itself counting as one.
The corpus holds one document for each in oer-conformance/unreadable/, among them a
repeated figure inside a receipt and nesting far past any parser's limit. A vector in
edge_vectors.json holds the extremes a document may carry — the largest binary64 as
a float and as an integer, a float just below the midpoint, a surrogate pair, nesting
exactly 64 deep — so a verifier that refuses too much fails too.
Decimal strings. A decimal string is JSON's own number grammar (RFC 8259 §6)
carried as a string, in ASCII: an optional - and no other sign, no whitespace, no
leading zero, digits on both sides of any ., and an exponent of one to three
digits. The significand — every digit before the exponent, leading zeros included —
has at most 34 digits, so every decimal string is exact in IEEE 754 decimal128.
Where a check reads a quantity it accepts a decimal string or an integer in the range
above, and nothing else. A receipt member that fails is malformed; a ledger figure
that fails makes the identity fail.
The canonical form is pinned independently of the vectors. The reference
generator uses the reference verifier's canonicaliser, so a defect in the field
order would be baked into every receipt id and then confirmed by the same code —
reversing the declared order and regenerating still yields a passing corpus. Every
verifier therefore also checks a canonical fixture: an input whose members are
given in the wrong order at two levels, against literal expected bytes and their
sha-256 id (sha256:364718c7…84ed30). The cross-check against the C kernel,
which shares no code with either verifier, proves the same property from the other
side.
A second fixture pins the stage list that bundle.provenance_root addresses (§6),
which is serialised by the same rules as the body. Every stage list in the corpus is
ASCII, and on ASCII text an escaped and an unescaped serialisation agree, so no
vector can tell them apart. The fixture's stages carry a two-byte and a four-byte
character, with their members out of order, against literal bytes and their
sha-256 id (sha256:c1caa0ce…f2638b).
A third fixture pins minor version 1's members (§3.1): a body whose energy.class and
whose class's own members are out of order, against literal bytes and their sha-256
id (sha256:b473c94d…388b94); and the same input under "2.0", which the canonical
form refuses.
5. Identity, chain and signature
receipt_id=H(canonical body).chain.receipt_hash=H(chain.prev_hash ‖ canonical body), the previous hash as UTF-8 bytes. Every receipt after the first MUST carry aprev_hashequal to the previous receipt'sreceipt_hash, both strings — a hash that is not a string links nothing, so the receipt after it is achain_break.chain.signatureis Ed25519 (RFC 8032) over the UTF-8 bytes ofchain.receipt_hash, verified against the key the vector file publishes underchain.signer. A published key is decoded strictly (RFC 8032 §5.1.3): it MUST be the canonical encoding of a point, and the point MUST NOT be of small order, which verifies a signature over any message. Every signature under a key that fails either issignature_invalid.
H is named by hash_alg: sha-256 writes sha256:<hex>, blake3 writes
blake3:<hex>. A verifier that lacks the named algorithm MUST report
missing_capability — never a mismatch, which would blame the producer for the
verifier's gap. sig_alg MUST be ed25519.
6. Composition checks
Every check is evaluated in exact decimal arithmetic: no sum, product or
comparison is rounded, so no verdict is decided by rounding noise and two
conforming verifiers cannot disagree at a tolerance's edge. "Within t" means
|a − b| ≤ t — a difference exactly at the tolerance passes, and a miss counts the
same on either side — and every relative tolerance scales by the magnitude of the
figure it names, so a negative figure is judged exactly as a positive one.
- Sum. The
energy.breakdownterms MUST sum tojoules_totalwithin1e-9 × max(1, |sum|). - Floor.
floor.floor_jMUST equalbits_committed · k_B · temperature_k · ln 2within 1% of that product's magnitude — the Landauer floor for the bits the decision committed — withk_B = 1.380649e-23 J/K(exact by definition) andln 2taken as the decimal0.6931471805599453, so the product itself is exact. - Bundle.
bundle.provenance_root=Hof the stage list, each stage inbundle.stages[]member order, serialised by §4's rules exactly as the body is; the stages'joulesMUST sum tojoules_totalwithin1e-9 × max(1, |sum|); a bundle MUST carryefficiency.completed_units. - Records. A receipt served from a record (
settlement.served_from_record, with anyrecord_idbutunverified) citessettlement.record_hash, which MUST equalsha256("<key>|<answer>|<created>")over the record the vector publishes, withcreatedwritten to one decimal place rounded half-to-even on its binary value. A record'skeyandanswerare strings and itscreateda number (not a boolean); a record that is not, arecord_idthat is absent, empty or not a string, and a record store that is not an object cannot vouch for anything, so the receipt citing it is arecord_mismatch. The check runs for a vector that publishesrecords; one that publishes none makes no claim about them and runs no record check. - Ledger. The receipts'
joules_totalplus the ledger'sunallocated_joulesandcarried_forward_joules(0when absent) MUST equal itsmeter_jouleswithin1e-9 × max(1, |meter|). A ledger figure that is not a quantity (§4) makes the identity fail, and so does a receipt that states the unmetered marker (§3.1): a meter's reading cannot be accounted for by a receipt that states no figure.
7. Reasons
A verifier reports every reason a receipt fails, from this vocabulary, and nothing else, and a receipt too broken to check is a reason, never a verifier error. Some checks stop at the first failure and report it alone, discarding anything found before it; they run in this order, so a receipt with more than one names the same reason everywhere:
- the receipt is not a JSON object —
malformed; oeris absent, not a string, or of another major —unknown_major(§3);- a member not listed for its path,
chainincluded —canonical_failure; - the body carries a number §4 forbids —
malformed; hash_algnames an algorithm the verifier lacks —missing_capability;- any other member a check needs is absent or mistyped,
hash_algandbundle.stages(not a list) included —malformed.
Everything else accumulates. Some members are judged on their value, so a wrong
one is a named reason rather than malformed:
- judged whether absent or mistyped —
kindthat is not a listed string isunknown_kind;energy.boundarythat is not a non-empty string isboundary_missing; a bundle'sefficiencythat is not an object holdingcompleted_unitsisefficiency_missing;sig_algother thaned25519ismissing_capability, which, unlike an unsupportedhash_alg, stops nothing; - judged when present but mistyped, and
malformed(step 6) when absent — areceipt_idthat is not a string isreceipt_id_mismatch; achain.receipt_hashthat is not a string isreceipt_hash_mismatchand, since the signature is over it,signature_invalid; achain.signerthat is not a string, or names a key that is absent or not a string, issigner_unknown; aprovenance.howthat is not a string isgrade_unknown.
| reason | raised when |
|---|---|
unknown_major |
oer is absent, not a string, or of a major other than 2 (reported alone) |
unknown_kind |
kind is not one of §1's kinds |
canonical_failure |
a member not listed for its path (reported alone) |
malformed |
the receipt is not an object, a member a check needs is absent or mistyped (a quantity that is not a §4 decimal string or integer included), or the body carries a number §4 forbids (reported alone) |
missing_capability |
the verifier lacks hash_alg (reported alone), or sig_alg is not ed25519 |
receipt_id_mismatch |
receipt_id ≠ H(body) |
receipt_hash_mismatch |
chain.receipt_hash ≠ H(prev_hash ‖ body) |
chain_break |
prev_hash ≠ the previous receipt's receipt_hash, or either is not a string |
sum_mismatch |
breakdown does not sum to joules_total |
boundary_missing |
energy.boundary is absent, empty or not a string |
floor_mismatch |
floor_j is not the Landauer floor within 1% |
grade_unknown |
provenance.how is not a §2 grade |
abstention_settled |
an abstain receipt carries an answer or a record (§3) |
bundle_root_mismatch |
provenance_root does not address the stages |
bundle_sum_mismatch |
stages do not sum to joules_total |
efficiency_missing |
a bundle without efficiency.completed_units |
signer_unknown |
chain.signer is not a string naming a published key that is a string |
signature_invalid |
the signature does not verify, or the published key fails §5's strict decoding |
record_mismatch |
a cited record does not rehash to record_hash, or cannot be rehashed (§6) |
ledger_mismatch |
the ledger identity does not hold |
unmetered_invalid |
a receipt of minor version 1 states the unmetered marker beside a breakdown term that is not null, a grade other than projected, a class, or as a bundle (§3.1) |
class_invalid |
a receipt of minor version 1 states an energy.class that is not §3.1's: an unknown or legacy label, or evidence missing, extra or of the wrong form |
class_grade_mismatch |
a valid energy.class does not support the receipt's grade, one of §2's (§3.1) |
Beside its reasons, a verifier reports each receipt that states the unmetered marker
as unmetered, by its receipt_id, whatever else it reports of it. A vector that
declares unmetered, a list of receipt ids in the order of its receipts, passes only
when the verifier reports exactly those receipts unmetered.
Reasons for receipts that share a receipt_id accumulate; a repeated id
MUST NOT replace, and so hide, the reasons of an earlier receipt.
8. Conformance
reason-set-equivalence. For every vector in
oer-conformance/, a conforming verifier reports exactly the declared set of reasons — the valid vectors none, each tampered vector exactly its own — and both fixtures (§4) hold.
| file | vectors | produced by |
|---|---|---|
vectors.json |
9 | the reference generator: a simulated corridor day, two valid vectors and seven tampered twins |
firmware_vectors.json |
1 | the portable C kernel (settle/): 400 receipts, independent SHA-256 and Ed25519 |
edge_vectors.json |
99 | generate_edge_vectors.py: one case for each reason the upstream nine never exercise, each number shape and decimal-string defect §4 forbids and each limit from the accepting side, each bound and tolerance edge, each step of the order of the reasons reported alone, each member judged on its value, each claim an abstention cannot make, each key a signature cannot rest on, and the extremes a document may hold |
job_vectors.json |
38 | generate_job_vectors.py: job receipts of minor version 1 (§3.1) under oer.job.media.encode: metered hops of each class with the grade it supports, the unmetered marker on its own, in a failover chain and on a failed hop, each rule of the marker and of the class broken once, each class held to a grade it does not support, a ledger over a chain that holds the marker, and the same members under minor version 0 and a minor version this text does not define; every vector declares the receipts it states unmetered, and every expectation is written by hand |
unreadable/ |
11 documents | one for each way §4 refuses a document whole; every verifier must refuse all of them |
The upstream vectors cover eight reason names; the edge vectors pin the rest,
including malformed, canonical_failure and abstention_settled, the
repeated-id rule, and every edge a verifier could draw in the wrong place: each
integer bound from both sides; each decimal-string limit from both sides; each of
the four tolerances — sum, floor, bundle, ledger — exactly at its edge and just past
it, on both sides, for negative and small figures, and with the figure that sets its
base; for each of the four, a verdict that flips if it is computed to 34 digits
(and for the sum, to 28); each step of the order of the reasons reported alone; and
keys that are small-order or non-canonical encodings, each under a signature that a
lenient verifier accepts. Their expectations are written by hand from this document
— none is copied from a verifier's output, which would only prove the code agrees
with itself. The job vectors pin minor version 1's three reasons and its unmetered
report the same way.
Two rules bind the producer and no reason in §7 can enforce them: the capability namespace, and receipting an unearned decision as an abstention (§3). A verifier cannot tell from a receipt's bytes whether a verdict was earned — only that an abstention claims none.
wai_oer_vectors verify oer-conformance # wai-rs, --features oer: every file and unreadable/
python3 oer-conformance/oer2_verify.py <file> # reference verifier, per file
python3 oer-conformance/oer2_verify.py --unreadable oer-conformance/unreadable
python3 oer-conformance/generate_job_vectors.py # regenerates job_vectors.json byte for byte
python3 oer-conformance/fuzz_differential.py # the two verifiers against each other
make -C oer-conformance/settle && oer-conformance/settle/settle_host out.json 400 # the C kernel
The vectors pin reason-set-equivalence one case at a time. Between them,
fuzz_differential.py mutates corpus documents and requires the reference and
wai-rs to report the same reasons, or the same refusal, for every one, and neither
to crash.
CI runs wai_oer_vectors — every vector file and every document in unreadable/ —
with the other cargo-class corpora, rebuilds the C kernel, and checks its output
byte-for-byte against the pinned firmware_vectors.json and through
wai_oer_vectors.
9. Departures from draft 0.1
This extension settles the points the draft left open. Each changes what a conforming producer writes or what a verifier accepts; none changes how a receipt is chained or signed.
- Capability namespace. The draft's vectors named decisions
wai.settle.*, a namespace WAI reserves for payload formats. They areoer.decision.*here (§3), and the corpus is regenerated under the new names. - One serialisation. The draft computed
bundle.provenance_rootwith ASCII escaping and the body without it. Both use the body's serialisation here (§4, §6). Every stage list in the corpus is ASCII, where the two agree, so this rule alone moves no root — the corpus's roots differ from the draft's only because the rename changes every stage'sreceipt_hash. The stage-list fixture holds the rule. - The number profile is normative. Both producers already wrote only integers
and decimal strings; the draft did not require it. It is a MUST here (§4):
integers within I-JSON's range, decimal strings in JSON's number grammar, anything
else
malformed, and a document outside §4's document profile refused whole. That removes disagreements between JSON parsers the draft's vectors never exercised. - The checks are exact. The draft compared figures in whatever precision its
reference happened to use; here every check is exact, with
ln 2pinned as a decimal (§6), so a verdict never depends on rounding. - One tolerance rule. The draft's reference scaled the sum tolerance by the sum's magnitude but the floor, bundle and ledger tolerances by the signed figure, so a negative figure could fail a check it met. All four use the magnitude here (§6).
- Every shape has one reading. The draft's reference crashed, or reported
something other than its reason, on members of the wrong type — a list where a
string belongs, an object where a list does — and let a repeated member name hide
a second value. §4 refuses repeated names, §7 names the reason for each wrong
type, §5 says a link is a string, and §3's member lists hold inside
chaintoo. - Keys are decoded strictly (§5). The draft's reference accepted a small-order public key, and non-canonical encodings of one, under which a single signature verifies over every message.
oeris a string (§3), so the major version never depends on a float parse.- Abstention is its own outcome. The draft's
escalatereceipts carry the answer the escalation returned and store it as a record: they are verdicts, and §3 says so. A decision that was not earned isabstain, which claims no answer and stores no record, andabstention_settledholds it to that.