wai: live metering, a room session's units and joules per hop (L9) #317

Merged
dcharlot merged 21 commits from wai/live-metering into main 2026-10-18 19:03:46 -04:00
Owner

Based on #293 (joule-moq/relay), which has since merged into main (91de95ee). I cut this branch from origin/joule-moq/relay and merged origin/main into it (merges, never a rebase). It now includes main at 91de95ee, with #293, #315 and wai-quantum 0.6.3, so the diff against main is this PR's alone.

The relay change of this PR is joule-code-only commits:

  • joule-moq-relay: the per-hop session record of a WAI room
  • ... the session's opening tally in one initializer (clippy)
  • ... the metering test reads concurrently and ends its subscribers first
  • ... the metering test states what the record says of a closing session

What this is

Slice L9 of the live work: metering and energy for a room session, wai.live.metering.

  • wai/extensions/live-metering.md (new): the per-hop session record WMH1, the room-session receipt WMR1, verification, QoE observations, the C2PA mapping, and eighteen refusal reasons.

  • wai/wai-rs/src/live_metering.rs, feature live_metering (= live_compose + capture_take + telemetry). It is cbindgen:ignore and adds no dependency. It covers:

    • encoding, parsing and verifying records, ledgers and receipts;
    • aggregating inputs into a receipt (aggregate);
    • verifying a receipt against the room, takes and composition receipts (verify_session);
    • the CMCD event-timeline record and the application/cmcd batch body;
    • unit tests.

    capture_take::find_take (new) exposes capture-binding §9.2, so a take's committed capture can be counted.

  • c2pa_emit (with live_metering): SessionManifest writes a receipt into a real signed C2PA manifest, and recover_session reads it back.

  • wai/live-conformance/metering/: 45 vectors from fixed seeds, plus one record the real relay signed in its test (relay-record.wmh, relay-ledger.wml).

  • wai/live-conformance/live_metering_verify.py: the independent second implementation, standard library only.

  • joule-moq-relay: signs the per-hop record of a room from its session summaries. New test tests/room_metering.rs.

  • Registry and CI:

    • SPEC §5, REGISTRY, and the regenerated registry export (capabilities.json, vocab/v2.ttl);
    • wire identifiers WMH1 and WMR1;
    • the live/* authorities in the jwp-receipts §7.5 table;
    • WAI and joule-code CHANGELOG entries;
    • an independent-verifiers step, a C2PA emission step, and the spec added to both path lists;
    • the live-conformance exclusion reason in int_kernels_vectors, updated.

The design

Per-hop session record (WMH1, §3)

A relay, compositor or ingest gateway signs one record per room session. It holds:

  • the room, the hop's key and role, and the session span on the room clock;
  • its own energy: µJ with the class tag and evidence of energy-measurement §2.7, or the unmetered marker. A record with the legacy HwShunt tag is refused (energy_invalid).
  • per media track and direction: objects, bytes, drops by reason (delivery timeout, refused at the door, duplicate, queued at the end), and the dropped objects whose receipts were forwarded. The room's events tracks count as one entry per direction, because every chatting viewer has one; so do all its other tracks.
  • subscribers: subscriber sessions, and subscriber-µs in nine layer classes (layers 0–7, plus 8 for events-only).
  • events delivered: objects with Object ID ≥ 1 on events tracks. Join sets are not counted.
  • the root and count of a ledger: one 143-byte entry per grant its sessions ran under, with that grant's ceilings, bytes in and out, and subscriber time. The ledger travels beside the record, unsigned. The record signs its RFC 6962 root, using the staged-measurement node, so each entry can be shown with its audit path.

Room-session receipt (WMR1, §4)

It names:

  • the hop records, by digest;
  • the take manifests;
  • the composition receipts: a count, and a digest over their sorted digests;
  • the QoE logs document: a count, and its SHA-256.

It states:

  • units: publisher-µs, subscriber-µs by class, sessions, composition-µs, events sent and delivered, recorded-µs;
  • every hop's figure, restated with its class;
  • one total per class: a total of tag 0 is refused classes_summed, and so is a total of class X that equals X's sum plus one or more other classes' sums;
  • a tariff;
  • the grants of the session: their number, a consumption root, and every grant past a ceiling;
  • a QoE cell.

A verifier recomputes every number from the records it names (§5.1 steps 1–11).

Design choices to check

  1. The metering power is a key-log authority, live/meter, not a grant power.

    • A receipt states every principal's consumption, the authority's own ceilings included. A grant power would let one participant sign another's bill.
    • live-rooms' four power bits are a closed set (§3.4), and L2 was not to change.
    • The key log already gives revocation and anchoring, as capture/store does for the store.
    • The signer, the room authority included, also needs energy/<class> for every class it totals (jwp-receipts §7.5).
    • Hops need live/relay, live/compositor or live/ingest, plus energy/<class> for a metered figure.
    • A room with no origin has no key log. Only its authority signs receipts, and a hop must be a room key.
  2. Size bound: B(M) = 552 + 180 × (M + 2) bytes.

    • M is the number of distinct media tracks the room published in the session, from the lifecycle replay. It does not depend on the audience.
    • 552 = 281 fixed bytes + 271 bytes of the longest class evidence (tag 5, with a 255-byte reference).
    • The tracks part allows two entries per media track, plus two each for events and other tracks, at 90 bytes each.
    • The bound is checked before the record is read.
    • In the large-audience vector, 10,000 extra subscriber sessions leave the relay record the same size as the lesson's, to the byte. The ledger grows with grants; the record does not.
    • That is my reading of "batch per group, never per object per viewer": nothing is per object or per viewer, and multi-record hops cut at group boundaries (§3.8).
  3. Privacy rules (§6.4).

    • Only three things enter the receipt: the count of logs, the digest of the logs document, and one cell.
    • The cell is suppressed below 50 sessions (delivery-telemetry §8.4). A receipt with an unsuppressed small cell is refused qoe_not_suppressed.
    • The observations, sid, timestamps, URLs and per-session metrics stay with the session and the room's service.
    • A CMCD track goes only to the room's service, never to the room's participants. It is named with the fresh sid, never a principal key.
    • The cell is neither per grant nor per layer class. QoE is labelled as unsigned statements, never evidence.
  4. Joules attributed to a grant are an allocation, not a measurement.

    • Each metered hop's figure is split over its ledger entries by bytes moved, rounded down, with the remainder attributed to no grant. Each share keeps the figure's class.
    • The joule-ceiling verdict compares the cross-class sum with the ceiling, but that sum is never stated as a figure.
    • Joule verdicts are checked in order: none, past, unmetered, partial, within. Funds verdicts: none, no_tariff, past, within.
    • Past a ceiling, nothing new is defined; live-rooms §4.5 stands. A meter that ends a use does so through leave (reason 2) or a grant exchange. The receipt counts what happened and names the grant.
  5. Funds come from a stated tariff. Prices are u32 millionths of a minor unit, per second of each unit and per event sent. Funds = ⌈N / 10^12⌉, saturating at 2^64 − 2; 2^64 − 1 means not computed.

  6. Composition-seconds and recorded-seconds.

    • Composition-seconds: the room-clock gaps between consecutive outputs of one composed track (same compositor, media and output track) that are at most 1 s apart. A lone receipt adds nothing.
    • Recorded-seconds: only capture the device committed while capturing. That is the chunks below the last held commitment's size, mapped with the first held commitment's offset, clipped to the session.
  7. CMCD checks.

    • Against CTA-5004-B's application/cmcd: a batch is one report per LF-separated line, with no trailing LF, sorted keys and no URL encoding. delivery-telemetry's Event-mode reports are such lines already. CTA-5004-B cites RFC 8941, which RFC 9651 obsoletes, and the reports use no type RFC 9651 added.
    • On draft-wilaw-moq-cmcd-event-timeline-00, sf and the WAI custom keys are not on the permitted list, so they are not carried, and e=rr is barred.
    • That draft's example writes Integers as JSON numbers, but its normative text says strings. I followed the text.
  8. The relay's session summary format moves to /2. The summary gains:

    • per-track publisher counts;
    • fetch bytes;
    • objects queued at the end;
    • objects with Object ID ≥ 1;
    • per-room layer spans;
    • the room grant, from [[metering.grants]], keyed by token hash.

    A datagram's bytes are now counted in bytes received.

    The sched.rs edits are tally counters only: an object_id on ObjMeta, and per-track fetch bytes, incremental objects and queued-at-close counts. No ordering, timeout or flow logic changed. The base merge brought in Connection::diagnostics and Relay::session_diagnostics with no conflict.

Implemented vs specified

  • Implemented:
    • the record, ledger and receipt, aggregation and verification;
    • the relay emitting a real record;
    • the QoE cell from delivery-telemetry's session metrics;
    • cmcd_timeline_record and cmcd_batch_body;
    • C2PA, in c2pa_emit with the real SDK:
      • c2pa.environmental-sustainability carries energy_kwh only when one class covers the session whole;
      • GHG and water are written only when supplied, with their method;
      • the companion science.transaction.wai.live.metering is always written;
      • two tests sign a PNG, validate it Valid, and recover the assertions.
  • Specified only:
    • compositor and ingest-gateway emitters. Their records in the corpus are hand-built from simulated sessions, and their constant figures were measured by nothing; the corpus README says so;
    • a client publishing the CMCD track (the function exists, and no client calls it);
    • multi-record hops (§3.8);
    • hop links between relays: the reference relay connects to no other hop.

Corpus and verifiers

  • Accepted (6):
    • lesson: relay and compositor both on an on-chip counter, two takes, ten composition receipts, three logs (cell suppressed), a EUR tariff, signed by the metering key;
    • signed_by_authority;
    • mixed_classes: counter, model and estimate, three totals;
    • ceilings_past: one grant past its joule ceiling, another past its funds ceiling;
    • large_audience: 10,000 extra sessions, the same record size, 60 logs with the cell published;
    • relay_recorded: the real relay's record, unmetered, so coverage is partial.
  • Refused (39): every reason of §9 at least once, plus pass-through codes from rooms, takes and composition receipts.
  • The real relay record. joule-moq-relay's room_metering test signed it on the build host on 2026-10-18:
    • a 3.196 s span, 3 subscriber sessions, 36 events, 5 ledger entries;
    • the subscribers read concurrently, and their sessions end before the publishers';
    • the later change to that test only alters its assertions about a closing session, so the recorded pair is the run of the test's current behaviour;
    • the generator reads the record with include_bytes! and writes the relay room's lifecycle around its span;
    • the relay test hardcodes that room's id and grant digests, which wai_live_metering_vectors relay-room prints.
  • live_metering_verify.py:
    • agrees with Rust on all 45 vectors and fails all 23 mutated copies;
    • checks that every §9 reason refuses a vector and that §9 lists exactly its reasons;
    • runs in about 5 s.

rb commands and exit status

All cargo work ran on the build host through ~/kani-logs/remote/rb, with logs on the host. Each status is rb's, which is the command's own.

# rb command exit
1 rb . -- cargo build --no-default-features --features live_metering --lib --tests 0
2 rb --pull . -- wai_live_metering_vectors relay-room; … gen ../live-conformance/metering 101: the record_invalid vector restated role 4 in the receipt and was refused at the receipt. Generator fixed.
3 rb --pull . -- … gen 0
4 rb --pull . -- WAI_METERING_FIXTURE_DIR=… cargo test -p joule-moq-relay 101: compile error in metering.rs, fixed
5 the same 0: 22 unit tests and every integration binary, room_metering among them; the pair recorded
6 rb --pull . -- gen with the relay's record; cargo test --lib live_metering::; --test live_metering 1: two test mistakes (a funds expectation; a hop key's hex offset), fixed
7 rb . -- python3 -I live-conformance/live_metering_verify.py live-conformance/metering 0: 45 vectors, 23 mutations caught
8 rb . -- cargo test --no-default-features --features "live_metering c2pa_emit" --lib -- c2pa_emit:: live_metering::tests:: 0: 20 passed
9 rb --pull . -- checks (see below) 1, from the script's last grep finding no lockfile error. Every step in it was 0.
10 rb . -- final checks (see below) 0
11 after merging #293's 159d7ad9: rb . -- cargo test -p joule-moq-relay ×3 run 2 failed in room_metering. A publisher's close ended a subscriber's media subscription before the subscriber's own session ended, so the subscriber briefly held only a receipts track (class 8). The accounting is correct; the test raced. Fixed.
12 rb . -- full relay suite; --test room_metering ×10; cargo clippy -p joule-moq-relay --all-targets 0: suite green, room_metering 10 of 10, no notes from the crate
13 rb --pull . -- re-record the pair; gen 0
14 rb . -- … verify; --test live_metering; --lib live_metering::; live_metering_verify.py 0: all pass, 45 vectors
15 after CI job 34155: rb . -- room_metering binary, 16 concurrent loops of 4 runs (64), with the failure-only span report 0: 64 of 64 pass with the new assertions. 1 of 64 had class-8 time inside a session's close, which the old assertion would have failed. Load average ~97.
16 after merging origin/main at 91de95ee (#293, #315, wai-quantum 0.6.3; CHANGELOG conflict resolved keeping every entry): rb . -- cargo test -p joule-moq-relay; clippy; registry export --check; metering vectors verify; --test live_metering; --lib live_metering:: codecs::registry; int_kernels_vectors (cargo-class features); clippy live_metering; live_metering_verify.py 0 for each step. Relay suite 13 binaries ok; no clippy notes in the relay or the new code; registry export current; 45 vectors in Rust and in Python; 23 lib tests; int_kernels_vectors 8 passed; clean tree.

Run 9's steps:

  • touch cbindgen.toml then a build: include/wai.h unchanged;
  • wai registry export, then --check: 0, 0;
  • --lib codecs::registry: 0 (14 passed);
  • --test live_metering: 0;
  • capture_take lib tests: 0;
  • int_kernels_vectors with the cargo-class step's exact features (neural_int,video_int,audio_int,world_int,splat_int,generate,score,staged,stage_measure,legacy_int,i32_forms): 0 (8 passed);
  • clippy for live_metering and for the relay: 0, with 4 notes on my code, fixed afterwards;
  • scripts/check-lockfiles.sh: 0.

Run 10's steps, all 0:

  • --lib live_metering:: (9 passed);
  • --test live_metering (4);
  • wai_live_metering_vectors verify (both PASS lines);
  • --test capture_take (4);
  • c2pa_emit:: with live_metering (11);
  • registry export --check;
  • clippy, with no notes in the new code;
  • a clean tree after the builds;
  • the Python verifier (45);
  • cargo test -p joule-moq-relay, every binary;
  • relay clippy, no notes.

Locally (light scripts), all 0:

  • check-tests-in-ci.py;
  • check-workflow-concurrency.sh;
  • check-spec-refs.py;
  • check-wai-section-refs.py, after two references to delivery-telemetry's paragraph labels were moved to its §3;
  • check-no-vanity-metrics.sh;
  • check-comparative-prose.sh;
  • check-orphan-sources.py.

check-public-names.sh needs the secret denylist and runs on the push to main. I scanned the new text by hand.

The CI failure on d2fb5103, and its fix

What failed. Job 34155, joule-code conformance gate / joule-moq-relay, every target, failed at tests/room_metering.rs:235, "every subscriber held layer 0 of the room's video". The test assumed no subscriber time outside layer class 0.

Cause, reproduced under load. As a session closes, the relay ends its subscriptions one at a time, each when its request stream fails. A moment after the session's last media subscription has ended, but before its receipts or events subscription ends, is time on no media track. That time is class 8, as §3.3 defines.

I added a failure-only report of every subscriber session's layer spans, relative to the instant the test began closing them. I then ran the test 64 times (16 concurrent loops of 4) on the build host at a load average of about 97:

  • 1 of 64 runs had such time;
  • that session was class 0 until 2,329 µs into the close, then class 8 for 76 µs.

Decision: the classification is right, and the test's assumption was wrong. The session really held only a receipts and an events subscription in that interval.

The test now asserts what the record says. It requires:

  • class-0 time;
  • no time at any layer above 0;
  • class-8 time only after the test began closing the sessions.

On failure it reports every session's spans. All 64 runs passed.

Text. live-metering §3.3 and the relay README now say how a closing session is classed: the moments between subscription ends are classed by what is still held, typically microseconds of class 8 at a session's end.

What is left

  • A meter backing the relay: its figure is the unmetered marker.
  • Compositor and ingest-gateway emitters.
  • A client publishing the CMCD track.
  • The relay counting hop links, once relays chain.
  • A record over sessions still open.
  • Re-recording the relay pair after 2026-10-31 would fall outside the relay room's grants.
  • Offering the units as a candidate energy-metric registry (the design's contribution target) is an outward step, for the owner.
  • CI on the new head, e59bc14e.

🤖 Generated with Claude Code

**Based on #293** (`joule-moq/relay`), which has since merged into main (91de95ee). I cut this branch from `origin/joule-moq/relay` and merged `origin/main` into it (merges, never a rebase). It now includes main at 91de95ee, with #293, #315 and wai-quantum 0.6.3, so the diff against `main` is this PR's alone. The relay change of this PR is joule-code-only commits: - `joule-moq-relay: the per-hop session record of a WAI room` - `... the session's opening tally in one initializer (clippy)` - `... the metering test reads concurrently and ends its subscribers first` - `... the metering test states what the record says of a closing session` ## What this is Slice L9 of the live work: metering and energy for a room session, `wai.live.metering`. - **`wai/extensions/live-metering.md`** (new): the per-hop session record `WMH1`, the room-session receipt `WMR1`, verification, QoE observations, the C2PA mapping, and eighteen refusal reasons. - **`wai/wai-rs/src/live_metering.rs`**, feature `live_metering` (= `live_compose` + `capture_take` + `telemetry`). It is `cbindgen:ignore` and adds no dependency. It covers: - encoding, parsing and verifying records, ledgers and receipts; - aggregating inputs into a receipt (`aggregate`); - verifying a receipt against the room, takes and composition receipts (`verify_session`); - the CMCD event-timeline record and the `application/cmcd` batch body; - unit tests. `capture_take::find_take` (new) exposes capture-binding §9.2, so a take's committed capture can be counted. - **`c2pa_emit`** (with `live_metering`): `SessionManifest` writes a receipt into a real signed C2PA manifest, and `recover_session` reads it back. - **`wai/live-conformance/metering/`**: 45 vectors from fixed seeds, plus one record the real relay signed in its test (`relay-record.wmh`, `relay-ledger.wml`). - **`wai/live-conformance/live_metering_verify.py`**: the independent second implementation, standard library only. - **`joule-moq-relay`**: signs the per-hop record of a room from its session summaries. New test `tests/room_metering.rs`. - **Registry and CI:** - SPEC §5, REGISTRY, and the regenerated registry export (`capabilities.json`, `vocab/v2.ttl`); - wire identifiers `WMH1` and `WMR1`; - the `live/*` authorities in the jwp-receipts §7.5 table; - WAI and joule-code CHANGELOG entries; - an independent-verifiers step, a C2PA emission step, and the spec added to both path lists; - the `live-conformance` exclusion reason in `int_kernels_vectors`, updated. ## The design ### Per-hop session record (`WMH1`, §3) A relay, compositor or ingest gateway signs one record per room session. It holds: - the room, the hop's key and role, and the session span on the room clock; - **its own energy:** µJ with the class tag and evidence of energy-measurement §2.7, or the unmetered marker. A record with the legacy `HwShunt` tag is refused (`energy_invalid`). - **per media track and direction:** objects, bytes, drops by reason (delivery timeout, refused at the door, duplicate, queued at the end), and the dropped objects whose receipts were forwarded. The room's events tracks count as one entry per direction, because every chatting viewer has one; so do all its other tracks. - **subscribers:** subscriber sessions, and subscriber-µs in nine layer classes (layers 0–7, plus 8 for events-only). - **events delivered:** objects with Object ID ≥ 1 on events tracks. Join sets are not counted. - **the root and count of a ledger:** one 143-byte entry per grant its sessions ran under, with that grant's ceilings, bytes in and out, and subscriber time. The ledger travels beside the record, unsigned. The record signs its RFC 6962 root, using the staged-measurement node, so each entry can be shown with its audit path. ### Room-session receipt (`WMR1`, §4) It names: - the hop records, by digest; - the take manifests; - the composition receipts: a count, and a digest over their sorted digests; - the QoE logs document: a count, and its SHA-256. It states: - **units:** publisher-µs, subscriber-µs by class, sessions, composition-µs, events sent and delivered, recorded-µs; - **every hop's figure**, restated with its class; - **one total per class:** a total of tag 0 is refused `classes_summed`, and so is a total of class X that equals X's sum plus one or more other classes' sums; - **a tariff;** - **the grants of the session:** their number, a consumption root, and every grant past a ceiling; - **a QoE cell.** A verifier recomputes every number from the records it names (§5.1 steps 1–11). ## Design choices to check 1. **The metering power is a key-log authority, `live/meter`, not a grant power.** - A receipt states every principal's consumption, the authority's own ceilings included. A grant power would let one participant sign another's bill. - live-rooms' four power bits are a closed set (§3.4), and L2 was not to change. - The key log already gives revocation and anchoring, as `capture/store` does for the store. - The signer, the room authority included, also needs `energy/<class>` for every class it totals (jwp-receipts §7.5). - Hops need `live/relay`, `live/compositor` or `live/ingest`, plus `energy/<class>` for a metered figure. - A room with no origin has no key log. Only its authority signs receipts, and a hop must be a room key. 2. **Size bound: `B(M) = 552 + 180 × (M + 2)` bytes.** - *M* is the number of distinct media tracks the room published in the session, from the lifecycle replay. It does not depend on the audience. - 552 = 281 fixed bytes + 271 bytes of the longest class evidence (tag 5, with a 255-byte reference). - The tracks part allows two entries per media track, plus two each for events and other tracks, at 90 bytes each. - The bound is checked before the record is read. - In the large-audience vector, 10,000 extra subscriber sessions leave the relay record the same size as the lesson's, to the byte. The ledger grows with grants; the record does not. - That is my reading of "batch per group, never per object per viewer": nothing is per object or per viewer, and multi-record hops cut at group boundaries (§3.8). 3. **Privacy rules (§6.4).** - Only three things enter the receipt: the count of logs, the digest of the logs document, and one cell. - The cell is suppressed below 50 sessions (delivery-telemetry §8.4). A receipt with an unsuppressed small cell is refused `qoe_not_suppressed`. - The observations, `sid`, timestamps, URLs and per-session metrics stay with the session and the room's service. - A CMCD track goes only to the room's service, never to the room's participants. It is named with the fresh `sid`, never a principal key. - The cell is neither per grant nor per layer class. QoE is labelled as unsigned statements, never evidence. 4. **Joules attributed to a grant are an allocation, not a measurement.** - Each metered hop's figure is split over its ledger entries by bytes moved, rounded down, with the remainder attributed to no grant. Each share keeps the figure's class. - The joule-ceiling verdict compares the cross-class sum with the ceiling, but that sum is never stated as a figure. - Joule verdicts are checked in order: none, past, unmetered, partial, within. Funds verdicts: none, no_tariff, past, within. - Past a ceiling, nothing new is defined; live-rooms §4.5 stands. A meter that ends a use does so through `leave` (reason 2) or a grant exchange. The receipt counts what happened and names the grant. 5. **Funds come from a stated tariff.** Prices are u32 millionths of a minor unit, per second of each unit and per event sent. Funds = `⌈N / 10^12⌉`, saturating at 2^64 − 2; 2^64 − 1 means not computed. 6. **Composition-seconds and recorded-seconds.** - Composition-seconds: the room-clock gaps between consecutive outputs of one composed track (same compositor, media and output track) that are at most 1 s apart. A lone receipt adds nothing. - Recorded-seconds: only capture the device committed while capturing. That is the chunks below the last held commitment's size, mapped with the first held commitment's offset, clipped to the session. 7. **CMCD checks.** - Against CTA-5004-B's `application/cmcd`: a batch is one report per LF-separated line, with no trailing LF, sorted keys and no URL encoding. delivery-telemetry's Event-mode reports are such lines already. CTA-5004-B cites RFC 8941, which RFC 9651 obsoletes, and the reports use no type RFC 9651 added. - On `draft-wilaw-moq-cmcd-event-timeline-00`, `sf` and the WAI custom keys are not on the permitted list, so they are not carried, and `e=rr` is barred. - That draft's example writes Integers as JSON numbers, but its normative text says strings. I followed the text. 8. **The relay's session summary format moves to `/2`.** The summary gains: - per-track publisher counts; - fetch bytes; - objects queued at the end; - objects with Object ID ≥ 1; - per-room layer spans; - the room grant, from `[[metering.grants]]`, keyed by token hash. A datagram's bytes are now counted in bytes received. The `sched.rs` edits are tally counters only: an `object_id` on `ObjMeta`, and per-track fetch bytes, incremental objects and queued-at-close counts. No ordering, timeout or flow logic changed. The base merge brought in `Connection::diagnostics` and `Relay::session_diagnostics` with no conflict. ## Implemented vs specified - **Implemented:** - the record, ledger and receipt, aggregation and verification; - the relay emitting a real record; - the QoE cell from delivery-telemetry's session metrics; - `cmcd_timeline_record` and `cmcd_batch_body`; - **C2PA**, in `c2pa_emit` with the real SDK: - `c2pa.environmental-sustainability` carries `energy_kwh` only when one class covers the session whole; - GHG and water are written only when supplied, with their method; - the companion `science.transaction.wai.live.metering` is always written; - two tests sign a PNG, validate it `Valid`, and recover the assertions. - **Specified only:** - compositor and ingest-gateway emitters. Their records in the corpus are hand-built from simulated sessions, and their constant figures were measured by nothing; the corpus README says so; - a client publishing the CMCD track (the function exists, and no client calls it); - multi-record hops (§3.8); - hop links between relays: the reference relay connects to no other hop. ## Corpus and verifiers - **Accepted (6):** - `lesson`: relay and compositor both on an on-chip counter, two takes, ten composition receipts, three logs (cell suppressed), a EUR tariff, signed by the metering key; - `signed_by_authority`; - `mixed_classes`: counter, model and estimate, three totals; - `ceilings_past`: one grant past its joule ceiling, another past its funds ceiling; - `large_audience`: 10,000 extra sessions, the same record size, 60 logs with the cell published; - `relay_recorded`: the real relay's record, unmetered, so coverage is partial. - **Refused (39):** every reason of §9 at least once, plus pass-through codes from rooms, takes and composition receipts. - **The real relay record.** `joule-moq-relay`'s `room_metering` test signed it on the build host on 2026-10-18: - a 3.196 s span, 3 subscriber sessions, 36 events, 5 ledger entries; - the subscribers read concurrently, and their sessions end before the publishers'; - the later change to that test only alters its assertions about a closing session, so the recorded pair is the run of the test's current behaviour; - the generator reads the record with `include_bytes!` and writes the relay room's lifecycle around its span; - the relay test hardcodes that room's id and grant digests, which `wai_live_metering_vectors relay-room` prints. - **`live_metering_verify.py`:** - agrees with Rust on all 45 vectors and fails all 23 mutated copies; - checks that every §9 reason refuses a vector and that §9 lists exactly its reasons; - runs in about 5 s. ## `rb` commands and exit status All cargo work ran on the build host through `~/kani-logs/remote/rb`, with logs on the host. Each status is `rb`'s, which is the command's own. | # | rb command | exit | |---|---|---| | 1 | `rb . -- cargo build --no-default-features --features live_metering --lib --tests` | 0 | | 2 | `rb --pull . -- wai_live_metering_vectors relay-room; … gen ../live-conformance/metering` | 101: the `record_invalid` vector restated role 4 in the receipt and was refused at the receipt. Generator fixed. | | 3 | `rb --pull . -- … gen` | 0 | | 4 | `rb --pull . -- WAI_METERING_FIXTURE_DIR=… cargo test -p joule-moq-relay` | 101: compile error in `metering.rs`, fixed | | 5 | the same | 0: 22 unit tests and every integration binary, `room_metering` among them; the pair recorded | | 6 | `rb --pull . -- gen with the relay's record; cargo test --lib live_metering::; --test live_metering` | 1: two test mistakes (a funds expectation; a hop key's hex offset), fixed | | 7 | `rb . -- python3 -I live-conformance/live_metering_verify.py live-conformance/metering` | 0: 45 vectors, 23 mutations caught | | 8 | `rb . -- cargo test --no-default-features --features "live_metering c2pa_emit" --lib -- c2pa_emit:: live_metering::tests::` | 0: 20 passed | | 9 | `rb --pull . -- checks` (see below) | 1, from the script's last `grep` finding no lockfile error. Every step in it was 0. | | 10 | `rb . -- final checks` (see below) | 0 | | 11 | after merging #293's 159d7ad9: `rb . -- cargo test -p joule-moq-relay` ×3 | run 2 failed in `room_metering`. A publisher's close ended a subscriber's media subscription before the subscriber's own session ended, so the subscriber briefly held only a receipts track (class 8). The accounting is correct; the test raced. Fixed. | | 12 | `rb . -- full relay suite; --test room_metering ×10; cargo clippy -p joule-moq-relay --all-targets` | 0: suite green, room_metering 10 of 10, no notes from the crate | | 13 | `rb --pull . -- re-record the pair; gen` | 0 | | 14 | `rb . -- … verify; --test live_metering; --lib live_metering::; live_metering_verify.py` | 0: all pass, 45 vectors | | 15 | after CI job 34155: `rb . -- room_metering` binary, 16 concurrent loops of 4 runs (64), with the failure-only span report | 0: 64 of 64 pass with the new assertions. 1 of 64 had class-8 time inside a session's close, which the old assertion would have failed. Load average ~97. | | 16 | after merging `origin/main` at 91de95ee (#293, #315, wai-quantum 0.6.3; CHANGELOG conflict resolved keeping every entry): `rb . -- cargo test -p joule-moq-relay; clippy; registry export --check; metering vectors verify; --test live_metering; --lib live_metering:: codecs::registry; int_kernels_vectors (cargo-class features); clippy live_metering; live_metering_verify.py` | 0 for each step. Relay suite 13 binaries ok; no clippy notes in the relay or the new code; registry export current; 45 vectors in Rust and in Python; 23 lib tests; `int_kernels_vectors` 8 passed; clean tree. | Run 9's steps: - `touch cbindgen.toml` then a build: `include/wai.h` unchanged; - `wai registry export`, then `--check`: 0, 0; - `--lib codecs::registry`: 0 (14 passed); - `--test live_metering`: 0; - capture_take lib tests: 0; - **`int_kernels_vectors` with the cargo-class step's exact features** (`neural_int,video_int,audio_int,world_int,splat_int,generate,score,staged,stage_measure,legacy_int,i32_forms`): 0 (8 passed); - clippy for `live_metering` and for the relay: 0, with 4 notes on my code, fixed afterwards; - `scripts/check-lockfiles.sh`: 0. Run 10's steps, all 0: - `--lib live_metering::` (9 passed); - `--test live_metering` (4); - `wai_live_metering_vectors verify` (both PASS lines); - `--test capture_take` (4); - `c2pa_emit::` with `live_metering` (11); - `registry export --check`; - clippy, with no notes in the new code; - a clean tree after the builds; - the Python verifier (45); - `cargo test -p joule-moq-relay`, every binary; - relay clippy, no notes. Locally (light scripts), all 0: - `check-tests-in-ci.py`; - `check-workflow-concurrency.sh`; - `check-spec-refs.py`; - `check-wai-section-refs.py`, after two references to delivery-telemetry's paragraph labels were moved to its §3; - `check-no-vanity-metrics.sh`; - `check-comparative-prose.sh`; - `check-orphan-sources.py`. `check-public-names.sh` needs the secret denylist and runs on the push to main. I scanned the new text by hand. ## The CI failure on d2fb5103, and its fix **What failed.** Job 34155, `joule-code conformance gate / joule-moq-relay, every target`, failed at `tests/room_metering.rs:235`, "every subscriber held layer 0 of the room's video". The test assumed no subscriber time outside layer class 0. **Cause, reproduced under load.** As a session closes, the relay ends its subscriptions one at a time, each when its request stream fails. A moment after the session's last media subscription has ended, but before its receipts or events subscription ends, is time on no media track. That time is class 8, as §3.3 defines. I added a failure-only report of every subscriber session's layer spans, relative to the instant the test began closing them. I then ran the test 64 times (16 concurrent loops of 4) on the build host at a load average of about 97: - 1 of 64 runs had such time; - that session was class 0 until 2,329 µs into the close, then class 8 for 76 µs. **Decision: the classification is right, and the test's assumption was wrong.** The session really held only a receipts and an events subscription in that interval. **The test now asserts what the record says.** It requires: - class-0 time; - no time at any layer above 0; - class-8 time only after the test began closing the sessions. On failure it reports every session's spans. All 64 runs passed. **Text.** live-metering §3.3 and the relay README now say how a closing session is classed: the moments between subscription ends are classed by what is still held, typically microseconds of class 8 at a session's end. ## What is left - A meter backing the relay: its figure is the unmetered marker. - Compositor and ingest-gateway emitters. - A client publishing the CMCD track. - The relay counting hop links, once relays chain. - A record over sessions still open. - Re-recording the relay pair after 2026-10-31 would fall outside the relay room's grants. - Offering the units as a candidate energy-metric registry (the design's contribution target) is an outward step, for the owner. - CI on the new head, e59bc14e. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A new crate in the joule-code workspace, library and binary:

- transports behind one interface: native QUIC (ALPN moqt-22) and WebTransport
  over HTTP/3 (ALPN h3, WT-Available-Protocols) on one quinn endpoint, and a
  WebSocket binding whose messages are draft-ietf-webtrans-http2-15 capsules;
- the relay: SETUP, PUBLISH_NAMESPACE, SUBSCRIBE_NAMESPACE, PUBLISH, SUBSCRIBE
  from an upstream it holds or makes, RENDEZVOUS_TIMEOUT, FETCH and fills from
  the cache or upstream, TRACK_STATUS, REQUEST_UPDATE, GOAWAY, Request ID rules,
  MAX_REQUEST_UPDATES, the authorization-token alias cache;
- a per-session scheduler in section 5.1.2's order with delivery timeouts at
  this hop; receipts tracks carry none;
- receipts checked at the door per namespace, and signed session summaries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
One scenario run over each transport on loopback: a publisher reached by the
namespace route and one by PUBLISH, three subscribers of both, one of them
discovering the first namespace by SUBSCRIBE_NAMESPACE. Every object reaches
every subscriber and verifies against its publisher's signed receipts, which
the relay checks at the door; every session's summary verifies under the
relay's key and labels the relay's energy unmetered.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The scheduler now bounds opening a stream, not only the write, by the
object's deadline; the relay's QUIC send window is configurable, so what
cannot be sent waits in the scheduler where delivery timeouts apply; the
test client can read lazily, holding flow control like a slow reader.

The test: a base track at priority 0 and a layer at 200 with a 100 ms
OBJECT_DELIVERY_TIMEOUT. A slow subscriber reads nothing for 1.5 s. Its base
track arrives whole; some layer objects pass their deadline and their
streams are reset with DELIVERY_TIMEOUT; every receipt of both tracks
arrives and each dropped object verifies by inclusion; the relay's signed
summary counts each layer object as forwarded or dropped, exactly.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A late joiner over the WebSocket fallback reads TRACK_STATUS's Largest Object,
FETCHes a whole track and its receipts track from the cache and verifies
every object, FETCHes a range in descending group order, is refused a range
past the Largest Object with INVALID_RANGE, and joins at a past group by a
SUBSCRIBE whose fill arrives on a fetch stream named by its Request ID. The
publisher is asked for nothing. A FETCH for a track the relay holds nothing
of goes to the namespace's publisher, and its fetch stream is piped through.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
- goaway.rs: every session gets GOAWAY with the New Session URI and Timeout;
  a new request after it is refused with GOING_AWAY; the sessions that leave
  close with NO_ERROR, and the one that stays is closed with GOAWAY_TIMEOUT.
- auth_tokens.rs: a token registered in SETUP authorizes a publisher; one
  registered in a request is used by alias in the next; no token or a wrong
  one is UNAUTHORIZED; an alias never registered closes the session with
  UNKNOWN_AUTH_TOKEN_ALIAS.
- priority_fan_out.rs: two publishers, four tracks, three subscribers with
  their own subscriber priorities and group orders, the subscribers' pipes
  held shut while the load arrives; each receives every object in section
  5.1.2's order.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
It does what moq-streaming-format 4.1 says a subscriber should not, and the
relay still applies no delivery timeout to a receipts track: every receipt
reaches it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Request ID of the server's parity, or one used twice, closes the session
with INVALID_REQUEST_ID; with a limit of two requests a third waits for
WT_MAX_STREAMS credit, which returns when a request is cancelled.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Let-chains where it collapsed nested ifs, values() where it iterated a map
for its values; three allows where the shape is deliberate.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The README states the draft the relay speaks and how each transport selects
it, the WebTransport library and why, how a browser connects (a certificate
hash in development; the WebSocket fallback), the WebSocket binding in full
as a non-normative binding of webtrans-http2 capsules, what the relay does
section by section, its scheduling and timeouts, receipts and the door, the
session summary and how its energy is labelled, and what is not yet built.

CI: a job of its own on linux-arm64 runs every target of the crate; the x86
workspace step excludes it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Read one stream at a time until a second passes with none, bounded by the
number of streams, rather than for a fixed window a loaded runner could
outlast.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge remote-tracking branch 'origin/main' into joule-moq/relay
Some checks failed
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m1s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 1m33s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 19s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m4s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 10m3s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Failing after 4m35s
beb2a8832b
The cache latched coverage on the first object it kept. When a group's
streams finished after a later group's, as they do on a loaded host, the
latch sat on the later group, and a FETCH from the earlier one went upstream
for objects the relay held: a late joiner on the arm64 runner was refused
when the publisher did not answer.

Coverage is now worked out from what is known of each group. A group is
whole when its start arrived (Object ID 0, or a Prior Object ID Gap), none of
its subgroup streams is open or was reset, and its end is known (its streams
ended with a FIN, or an End of Group or End of Track status, or a datagram
with END_OF_GROUP). A fetch is covered when every group from its start to the
Largest Object's is whole or known absent (a Prior Group ID Gap), that last
group has its start, and nothing between was evicted. A group never seen is
a gap the cache does not answer across.

The relay counts each upstream subgroup stream from its first kept object and
closes the count at its FIN (clean) or on any other end (the group is then
not whole). The late-joiner test now waits on the cache's coverage rather
than on objects_in, which counts arrivals before the cache; a new test sends
group 1 whole before group 0 and FETCHes from group 0, served from the cache.
It failed against the latch before this change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq-relay: the cache's test-only import where its tests are
Some checks failed
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m3s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 1m23s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 22s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m49s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 3s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m53s
4b8f08ba70
Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-code CHANGELOG: joule-moq-relay 0.1.0
Some checks failed
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m11s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 18s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 3m31s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m57s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m37s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Failing after 2m37s
4a8a6e340b
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The per-hop session record (WMH1), its ledger and size bound; the room-session
receipt (WMR1), the metering authority, units, joules per class, ceilings;
QoE on the CMCD event timeline; the C2PA mapping.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The relay signs WAI's per-hop session record (WMH1, wai/extensions/live-metering.md
section 3) of a room from its session summaries: per track and direction the objects
and bytes, the drops by reason with the receipts forwarded, its subscriber sessions'
time by layer class, the events delivered, and a ledger of the room grants its
sessions ran under, by the token each presented in SETUP. Its own energy is the
unmetered marker. Nothing in the record is per object or per viewer.

The session summary (format /2) keeps what the record is made from: per-track
publisher counts, fetched bytes, objects queued at the end and objects past Object
ID 0 per track, layer spans per room, and the room grant. tests/room_metering.rs:
two publishers, three subscribers, one slow; the record verifies and recomputes from
the summaries. No new dependency.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CI failed slow_subscriber once (39 of 40 base objects). Reproduced on the
build host under load: 3 failures in 64 runs, then 8 in 144. In every one the
relay's own tally for the base track was 40 forwarded, none dropped, none left
at close; no base stream arrived reset; the subscriber's one-at-a-time reader
had given up waiting on a stream's header with 22 to 88 accepted streams
unread behind it; and once those were read concurrently every missing base
object arrived. The relay lost nothing: the reader waited on a stream whose
bytes waited for connection credit the unread streams behind it held.

- The test now reads its streams concurrently, as a subscriber does, until
  the base track and every receipt are whole and a second passes with
  nothing new, or the overall deadline passes. Its assertions are unchanged.
  A failure report says where any missing base object was.
- The client keeps a stream next_data timed out on, rather than dropping it,
  records each such wait, and can hand out streams unread (next_stream).
- Connection::diagnostics gives a QUIC session's flow-control counters;
  Relay::session_diagnostics each session's.
- README: a subscriber that does not read stalls its own high-priority track
  through connection flow control; what the relay does about it (§5.2), and
  that it does not yet end such a subscription with TOO_FAR_BEHIND.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With its streams read concurrently, the test's drops rested on the stall
alone; under a load average near 200 a slow publisher was still sending when
the stall ended, and three runs of 144 dropped no layer object at all. The
subscriber now reads nothing until the publisher has sent everything and the
stall has passed after that, so every layer object waiting then is past its
deadline. 144 of 144 runs passed at a load average of 204 to 244.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
joule-moq-relay: clippy's notes on the new test code
Some checks failed
wai formal verification (integer core) / kani (rans64-t2) (pull_request) Has been cancelled
wai formal verification (integer core) / kani (stage-long) (pull_request) Has been cancelled
wai formal verification (integer core) / kani (integer-core) (pull_request) Has been cancelled
WAI energy receipts / does this change touch it (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI conformance / does this change touch it (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
wai cross-architecture byte-equality / does this change touch it (pull_request) Has been cancelled
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Has been cancelled
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Has been cancelled
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m3s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 17s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 1m31s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m29s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 8m20s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 2m50s
159d7ad9e3
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The subscribers read their streams concurrently with the base's collect_concurrent,
the slow one only after the load has arrived, as slow_subscriber.rs now does. Their
sessions end before the publishers', so no subscription ends while the subscriber
still holds another and the layer classes are what the test states.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge remote-tracking branch 'origin/main' into wai/live-metering
Some checks failed
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 19m4s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 30s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m26s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m23s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m10s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 3m23s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m18s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m16s
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been cancelled
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Has been cancelled
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been cancelled
d2fb510312
# Conflicts:
#	wai/CHANGELOG.md
As a session closes the relay ends its subscriptions one at a time, so a moment after
its last media subscription ended and before its receipts or events ones did is time
on no media track, class 8. Under load this happened in 1 of 64 runs (76 us, 2.3 ms
into the close) and failed the test's assumption that there is none. The test now
requires no time above layer 0 and class-8 time only within a session's close, and
reports each session's spans on failure; the README says how a closing session is
classed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai: live-metering §3.3, how a closing session is classed
All checks were successful
standards workspaces build + test / macOS energy backends (pull_request) Successful in 10m46s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 5m26s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 20m25s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 25s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m4s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 2m15s
wai-pack / wai-pack · macos (pull_request) Successful in 1m7s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 3m10s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 1m46s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 20s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 2m8s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 12s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 33s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m45s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m28s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 15m1s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 16m26s
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Successful in 46m2s
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Successful in 4m20s
WAI HTTP delivery / HTTP corpus · falsify mutations 1/3 (pull_request) Successful in 44m44s
WAI HTTP delivery / HTTP corpus · falsify mutations 2/3 (pull_request) Successful in 45m4s
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Successful in 4s
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Successful in 5m3s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m8s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m8s
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Successful in 4m54s
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Successful in 2m35s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 24s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m5s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m15s
e59bc14e62
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge origin/main into wai/live-metering; CHANGELOG keeps every entry
Some checks failed
WAI HTTP delivery / HTTP corpus · falsify patches 0/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 1/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify patches 2/3 (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · every patch and mutation in exactly one shard (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · verified both ways, and the differential (pull_request) Has been cancelled
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Has been cancelled
WAI energy receipts / does this change touch it (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI conformance / does this change touch it (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
standards workspaces build + test / which workspaces this change touches (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (pull_request) Has been cancelled
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Has been cancelled
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
fa9694b953
Main gained wai-quantum 0.6.4 and other merges; the only conflict was the CHANGELOG's
top insertion, and no other file is changed on both sides.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!317
No description provided.