wai: sink energy — sink profiles, a measured decode table, segment receipts and the per-hop split #460

Closed
dcharlot wants to merge 0 commits from wai/energy-sink-metering into main
Owner

What this is

WAI's sink energy (EN-A of the energy program): what decoding cost at the sink, per stream and per
segment, with the acquisition class and uncertainty of extensions/energy-measurement.md, a measured
per-capability decode table on each reference sink reached, and one session's energy split by hop.

  • Sink energy profiles (energy-measurement §9). Per platform: the counter, the fabrics its
    reading covers (cpu, gpu, media, neural), how the reading is the sink's (task: the
    operating system's account of the sink's own processes; bracket: §3's marginal over an idle
    bracket on a counter every user shares), the idle baseline, the sampling interval and the minimum
    window. macos-task (proc_pid_rusage V6: ri_energy_nj + ri_billed_energy − ri_serviced_energy,
    children read before they are reaped), macos-gpu (IOReport GPU Energy), linux-rapl (powercap
    package zones), linux-nvml (total energy behind a plausibility rule). All OnChipCounter,
    filtering Unknown, never HwShunt. No counter is divided among its users by busy time; a whole
    rail is one user's only at ≥99% of the busy time (rail). Phones and pages: estimator-cpu-time/1
    with declared inputs and a stated uncertainty. Display: estimator-display/1 from the panel's
    declared on-mode power and its luminance setting, never added to a decode figure.
  • wai.sink.energy (extensions/sink-energy.md): three JSON records signed with Ed25519 over
    RFC 8785.
    • Decode table (§4): one figure per (capability, path, decoder, profile), per completion of a
      stated work item, from 7+ rounds (odd) of bracketed measurement opened from rest. Every round's
      values are in the record; every counter reading is in a readings file bound by digest; a verifier
      recomputes each round, each round's figure and least declaration (§6.2), the median round, the
      range, the verdict and the declaration (which also covers the rounds' spread). Rows with no
      decoder, no work item, no hardware path or no live counter are absent and say which. Procedure
      wai-sink-table/1 = binary wai_sink_table.
    • Segment receipt (§5): per segment per stream, chained; figure measured over a bounded
      window (exactly the segment's decode, so no completion-count term), from the sink's own table
      (ModelBased) or another key's (Estimator), estimated, or unmetered with its reason. It is the
      sink-decode hop's figure for its segment.
    • Session receipt (§6): the per-hop split as the stream log's hop table (roles 1 origin, 2
      relay, 3 sink decode, 4 display): a figure with its own class, the unmetered marker with a reason
      where the hop took place unmeasured, or no entry where the sink states nothing. Origin/relay
      restate a hop's record (relay: live-metering WMH1) only where the session was the hop's sole
      user (≥99% of its busy time); a shared hop is the marker hop_shared.
  • Readers (wai-rs sink_meter): the four profiles, bound at run time (no build dependency),
    each with a survey status (live, frozen, needs-privilege, no-counter, not-implemented,
    implausible).
  • Corpus wai/live-conformance/sink-energy/: 51 cases (accepted and refused; every §8 reason
    refuses at least one), byte-stable from fixed seeds; plus the measured tables under tables/ with
    their readings and procedure.
  • Independent verifier wai/live-conformance/sink_energy_verify.py: written from the text by a
    separate agent that never read wai-rs; integer arithmetic only; 30 mutation kinds, each required
    to fail; --table <dir> for one measured table.
  • CI: wai-conformance.yml runs the integer checks; energy-hardware.yml gains sink-table-linux
    (linux-hw) and sink-table-macos (macos), which survey the runner's counters first and, where
    none is live, mark the measurement step skipped with the survey in the summary — never passed.
  • Registry entry wai.sink.energy, SPEC §5 paragraph, CHANGELOG, browser-sink §6 cross-reference.

Measured tables (per completion; 7 rounds; median, range)

Each row is one completion of the stated work item, sealed OnChipCounter (filtering Unknown),
Partial coverage; the figure is the median of 7 rounds, the declaration covers both the median
bracket and the rounds' spread. Figures under different profiles are different scopes and are not
compared with each other.

macOS arm64 sink, 32 cores (24 + 8), profile macos-task (tables/macos-arm64, taken
2026-10-22 at host load 68 → 22; the per-process counter is not entered by other work). Survey:
macos-task live; macos-gpu frozen at 200 ms (the rail republishes in steps), so every GPU
row is absent no_counter.

capability path work item µJ per completion range
wai.image.png software 1 image 1920×1080 39 351 37 133 – 43 136
wai.image.jpeg software 1 image 1920×1080 35 979 34 696 – 37 626
wai.image.avif software 1 image 1280×720 77 256 75 020 – 83 349
wai.image.jxl software 1 image 1280×720 72 071 70 471 – 75 429
wai.audio.opus software 10 s 37 420 36 236 – 40 641
wai.audio.flac software 10 s 15 202 14 998 – 16 557
wai.text.zstd software 4 MiB 5 272 5 226 – 5 370
wai.text.xz software 4 MiB 36 121 35 010 – 38 532
wai.video.av1 (WAI's decoder) software 30 frames 1280×720 2 158 696 2 013 449 – 2 696 721
wai.video.int_motion software 8 frames 96×96 10 725 10 194 – 11 361
wai.neural.int_hyper software 1 image 16×16 253 249 – 271
wai.neural.int_synth software 1 image 128×128 91 693 81 547 – 107 889
wai.audio.int_codec software 512 ms 7 139 5 361 – 7 732
wai.world.model software 9 frames 64×64 29 017 22 585 – 34 155

Hardware against software through the platform's media tool, 60 frames 1920×1080 per completion,
one decoder process per completion (process start included), the hardware path's output MD5
identical to the software path's:

capability software (µJ) hardware (µJ)
wai.video.avc 813 574 (746 208 – 897 657) 339 273 (317 648 – 396 031)
wai.video.hevc 993 261 (856 930 – 1 130 816) 325 831 (282 287 – 361 154)
wai.video.av1 702 494 (597 035 – 743 501) 360 325 (331 915 – 375 465)
wai.video.vp9 657 926 (612 448 – 688 726) 353 286 (344 104 – 424 094)

The hardware figures are Partial: they count the decoding process and the work billed to it
(the platform's decoder service), and nothing of the media engine, for which this host has no
channel at all (energy-measurement §9.2). They are not a statement that hardware decode costs that
little.

Linux x86-64 sink, 32 cores, profile linux-rapl (tables/linux-x86_64-32-core, quiet host,
package counter readable through its energy group). This build has no codec wrappers (no codec
development libraries on the host), so the integer decoders only:

capability work item µJ per completion range
wai.video.int_motion 8 frames 96×96 21 847 17 795 – 29 562
wai.neural.int_hyper 1 image 16×16 740 677 – 750
wai.neural.int_synth 1 image 128×128 467 945 416 570 – 473 106
wai.audio.int_codec 512 ms 45 176 36 996 – 45 597
wai.world.model 9 frames 64×64 198 411 114 293 – 212 064

Linux x86-64 sink, 20 cores, with a discrete GPU, profile linux-rapl
(tables/linux-x86_64-20-core): taken once the host had been idle for a minute (no CI job, load
below 1). Two earlier runs there fell on its own CI jobs and gave every row unmetered
(no_marginal, below_attribution_floor): a machine-wide counter read on a busy host refuses its
rounds rather than report a low figure. Integer decoders only, as on the 32-core host:

capability work item µJ per completion range
wai.video.int_motion 8 frames 96×96 23 908 22 267 – 26 247
wai.neural.int_hyper 1 image 16×16 777 723 – 840
wai.neural.int_synth 1 image 128×128 423 233 392 325 – 445 587
wai.audio.int_codec 512 ms 46 631 44 953 – 49 434
wai.world.model 9 frames 64×64 226 021 219 470 – 233 902

Its survey reads the GPU's total-energy counter at 13–23 W against the device's own ~10.4 W power
reading per 1 s interval: inside the plausibility rule, and read at 1 s, so it gives no rows in a
200 ms table.

Decisions and why

  • Profiles in energy-measurement, records in a new extension. The profiles (counter, fabrics,
    attribution, idle baseline, window) are §9 of energy-measurement as the brief asks; the three
    records are sink-energy.md, so the existing receipt families keep their bytes.
  • Existing fields, existing arithmetic. Every figure is written with jwp-receipts §2.4.5's
    members (joules_micro, energy_provenance, energy_uncertainty {relative_ppm, window_us},
    filtering, calibration reference) inside the record's signature, and every hardware figure comes
    from energy-measurement §6.2's integer forms. A segment's own bracket is bounded (it holds
    exactly that segment's decode), which is §6.2 with Wn = Wd = 1 and N = 0: §2.3's "removes
    the completion-count term by construction".
  • Repeats are in the record. A row is a figure only when every one of its rounds is a
    measurement (live rails, ratio ≥ 1, figure ≥ 1, declarable); the median round is sealed, the
    declaration is max(p_median, q) with q the rounds' range read as rectangular, and the
    median's §4 verdict decides figure / relative / unmetered (noisy). A failed round makes the
    row unmetered with that round's reason; nothing is dropped.
  • Raw readings are published and bound. Every interval ([nJ, µs]) of every bracket is in
    readings.json (RFC 8785 form, the bytes its digest covers); both implementations recompute
    every round from it.
  • The per-task counter on macOS. Every CPU, media and neural-engine channel of IOReport's
    Energy Model is frozen for an unprivileged reader on the build host; the kernel's per-process
    energy (with billed work, so a platform decoder service's CPU work counts) is live and is not
    entered by other work on a shared host, so it gives clean rounds at load 22–74 where a
    machine-wide counter cannot.
  • One hop table. The session's split uses the stream log's hop roles and its
    figure / unmetered-marker / absent rule (stream-termination §2, §3.5, open as #457), and a
    segment receipt is the sink-decode hop's figure for its segment.
  • One fabric vocabulary. cpu, gpu, media, npu; attribution task (with billed and
    serviced work) or rail (sole user ≥ 99%), as settled with the per-fabric record of #438. A
    counter every user shares gives no attribution: the figure is the bracket over the whole decode,
    not a fabric share, and neither it nor a Partial figure carries a division by fabric. A hop's
    figure is restated only for its sole user; a shared hop is the marker hop_shared. Nothing is
    divided by busy time.
  • Table method for live segments. A live sink usually cannot bracket each segment cleanly; its
    segment receipt can instead take its figure from its own table (ModelBased, calibrated on that
    host) or another key's (Estimator), checkable against the table row by integer arithmetic.
  • Display. estimator-display/1: the panel's declared on-mode power (its energy label's
    figure) scaled by the luminance setting and the time on screen; always Estimator, with the
    sink's stated uncertainty, never added to a decode figure.
  • No product names in the tables. The processor is described by architecture and cores;
    the procedure text names the open-source media tool and its digest for reproduction.
  • The table key is the corpus's fixed-seed key (it names the corpus, not a machine).

Checks

Build host = the arm64 macOS build host through rb; Linux = the 32-core x86-64 host (nice -n 19),
used as a second platform and while the build host's work volume was below rb's free-disk floor.

check where result
cargo test --locked --no-default-features --features sink_energy --lib sink_energy:: build host and Linux, at the head 9 passed
… --lib sink_meter:: build host (3 incl. the macOS task counter), Linux (2) passed
… --test sink_energy (corpus byte for byte, every case/reason/table) build host, Linux (HEAD) 2 passed
wai_sink_energy_vectors verify ../live-conformance/sink-energy build host PASS, PASS
wai registry export --check build host up to date
cargo test --locked --lib energy_report:: (Nat now crate-visible) build host 11 passed
cargo test --locked --lib codecs::registry build host 14 passed
cargo test --locked --no-default-features --features receipt_claims --lib claim_text (its gate widened) build host 2 passed
cargo metadata --locked --offline (no dependency added; no lockfile changes) build host ok
cargo clippy --locked --no-default-features --features sink_energy[,codecs,neural_int,video_int,audio_int,world_int] --all-targets, notes in the new files build host, Linux no note in the new files, either feature set, on macOS and on Linux
python3 -I live-conformance/sink_energy_verify.py live-conformance/sink-energy local 51 cases, every reason, 3 of 3 tables, 30 of 30 mutations: exit 0
python3 -I live-conformance/sink_energy_verify.py --table <dir> on each of the three measured tables local PASS
every other python3 -I verifier wai-conformance.yml runs (31) local all exit 0
python3 -I scripts/check-tests-in-ci.py, check-workflow-concurrency.sh, check-spec-refs.py, check-wai-section-refs.py, check-no-vanity-metrics.sh, test-public-text-checks.sh, check-comparative-prose.sh, check-orphan-sources.py local all exit 0
check-public-names.sh on the 28 changed files under wai/; commit messages against the patterns local clean; 0 hits
CI on the PR: every run cancelled on opening, as asked; before cancelling, the existing hardware job had failed on the host's missing GPU driver forge cancelled (13 runs)
merges of origin/main (b1681667, then 1a465f23; one CHANGELOG conflict, both entries kept): wfsteps.py HEAD 41c5bafba 761876d38 origin/main; ruby -ryaml over every workflow local 0 problem(s); all parse
the procedure itself (wai_sink_table run) macOS build host, both Linux hosts each table verifies as written, then independently
corpus generated on Linux = corpus generated on macOS Linux byte for byte (--test sink_energy on Linux against the macOS-generated file)

The pre-existing unpinned_read dead-code warning (int_payload) appears with any feature set
here and on main; not touched.

Falsifications

All committed first; each rule broken alone in a scratch copy, the guarding tests run, the file
restored (git diff --quiet clean after each).

rule broken (wai-rs) --lib sink_energy:: --test sink_energy
bracket liveness (sd_uw > 0, ≥ 5 live intervals) always true fails fails
unmetered_as_zero check removed passes fails (cases expecting it)
§6.4 sole-user rule removed passes fails
median taken as the least round fails fails
readings round comparison removed passes fails
§5.3 chain rule removed passes fails
spread term q forced to 0 fails fails
"ModelBased exactly for the sink's own table" removed passes passed at first: no case guarded it; added "another key's table sealed ModelBased", after which it fails

Python verifier (in a scratch copy beside the original, then removed): the §6.4 sole-user check
dropped → exit 1 ("a shared hop stated as the session's: expected split_hop_mismatch, got accept");
the half-up rounding of an interval's power dropped → exit 1 (self-test, and the 32-core table's
round 0 recomputes sd_uw 477 705 against the stated 477 704). Its own mutation self-test: 30
mutation kinds, every mutated copy fails.

CI skip semantics: the survey step's script, run against a survey with no live counter, writes
live=false and "SKIPPED (not passed)" to the summary, so the measurement step's
if: steps.survey.outputs.live == 'true' is skipped; against a live one it writes live=true.

Counters: a scripted counter that holds its value, advances in one lump, advances in 7 of 10
intervals, or fails a read is frozen (sink_meter::tests); on the real build host the GPU rail
reads frozen at 200 ms while idle, and those rows are absent no_counter rather than sealed.

Not done / open

  • The existing RAPL and NVML on jetson-hub job failed on this PR's first run ("NVML saw no GPU") while that host's GPU driver was not loaded after its restart on 2026-10-24; the host's state, not this change.
  • NVML. On that sink the total-energy counter, read every second, passes the plausibility
    rule while reading 1.4–1.9× the device's own power reading; read every 200 ms it fails. A
    plausible counter is read at its own 1 s interval, which a 200 ms table does not use, so no
    linux-nvml rows exist yet. After the host restarted on 2026-10-24 its GPU driver was not
    loaded, and the survey says no-counter.
  • No hardware decode path on Linux is reachable from the procedure: neither Linux host has a
    media tool installed, and the procedure states no platform media tool rather than inventing
    rows. The GPU rail on macOS is frozen at 200 ms, so the hardware rows there have no GPU figure.
  • The media engine has no counter an unprivileged process can read on macOS (no decoder
    channel exists), so every hardware row is Partial.
  • The existing reference meters' CPU reader (energy_burst::macmon_cpu_power, used by
    wai_meter, wai_video_meter, wai_world_meter, wai_quantum_meter, wai_prov, which seal
    OnChipCounter): on the build host every CPU channel of IOReport's Energy Model group is frozen
    for an unprivileged reader at 100 ms, 500 ms and 1 s, while that reader reports a varying CPU
    power. Its figure therefore does not come from a CPU energy channel shown live under §3.1 here,
    and §9.2 now says such a figure is ModelBased at best. Not changed in this PR (EN-C's ground).
  • Reconciliation with the stacked PRs, for the lead: the hop table is defined in
    stream-termination §2/§3.5 (#457) and cited here by name; it belongs in energy-measurement,
    beside §9 (a section of its own for the hop roles and the figure / marker / absent rule), so
    both extensions point to one place. The fabric vocabulary and task/rail match #438's settled
    terms; this text restates them in §9.1 until #438 lands, when §9.1 should cite §6.3 instead.
  • The macOS reader is a second implementation of #438's fabric meter: wai-rs cannot depend on
    wai-pack (which depends on wai-rs). Moving one meter into wai-rs for both is follow-up work.

🤖 Generated with Claude Code


Closed: Landed on main through merge train E (#499, merge bf39ec9a); verified by content: this PR's head is in main

## What this is WAI's sink energy (EN-A of the energy program): what decoding cost at the sink, per stream and per segment, with the acquisition class and uncertainty of `extensions/energy-measurement.md`, a measured per-capability decode table on each reference sink reached, and one session's energy split by hop. - **Sink energy profiles** (energy-measurement §9). Per platform: the counter, the fabrics its reading covers (`cpu`, `gpu`, `media`, `neural`), how the reading is the sink's (`task`: the operating system's account of the sink's own processes; `bracket`: §3's marginal over an idle bracket on a counter every user shares), the idle baseline, the sampling interval and the minimum window. `macos-task` (`proc_pid_rusage` V6: `ri_energy_nj + ri_billed_energy − ri_serviced_energy`, children read before they are reaped), `macos-gpu` (IOReport `GPU Energy`), `linux-rapl` (powercap package zones), `linux-nvml` (total energy behind a plausibility rule). All `OnChipCounter`, filtering `Unknown`, never `HwShunt`. No counter is divided among its users by busy time; a whole rail is one user's only at ≥99% of the busy time (`rail`). Phones and pages: `estimator-cpu-time/1` with declared inputs and a stated uncertainty. Display: `estimator-display/1` from the panel's declared on-mode power and its luminance setting, never added to a decode figure. - **`wai.sink.energy`** (`extensions/sink-energy.md`): three JSON records signed with Ed25519 over RFC 8785. - **Decode table** (§4): one figure per (capability, path, decoder, profile), per completion of a stated work item, from 7+ rounds (odd) of bracketed measurement opened from rest. Every round's values are in the record; every counter reading is in a readings file bound by digest; a verifier recomputes each round, each round's figure and least declaration (§6.2), the median round, the range, the verdict and the declaration (which also covers the rounds' spread). Rows with no decoder, no work item, no hardware path or no live counter are absent and say which. Procedure `wai-sink-table/1` = binary `wai_sink_table`. - **Segment receipt** (§5): per segment per stream, chained; figure `measured` over a bounded window (exactly the segment's decode, so no completion-count term), from the sink's own table (`ModelBased`) or another key's (`Estimator`), estimated, or `unmetered` with its reason. It is the sink-decode hop's figure for its segment. - **Session receipt** (§6): the per-hop split as the stream log's hop table (roles 1 origin, 2 relay, 3 sink decode, 4 display): a figure with its own class, the unmetered marker with a reason where the hop took place unmeasured, or no entry where the sink states nothing. Origin/relay restate a hop's record (relay: live-metering `WMH1`) only where the session was the hop's sole user (≥99% of its busy time); a shared hop is the marker `hop_shared`. - **Readers** (`wai-rs` `sink_meter`): the four profiles, bound at run time (no build dependency), each with a survey status (`live`, `frozen`, `needs-privilege`, `no-counter`, `not-implemented`, `implausible`). - **Corpus** `wai/live-conformance/sink-energy/`: 51 cases (accepted and refused; every §8 reason refuses at least one), byte-stable from fixed seeds; plus the measured tables under `tables/` with their readings and procedure. - **Independent verifier** `wai/live-conformance/sink_energy_verify.py`: written from the text by a separate agent that never read wai-rs; integer arithmetic only; 30 mutation kinds, each required to fail; `--table <dir>` for one measured table. - **CI**: `wai-conformance.yml` runs the integer checks; `energy-hardware.yml` gains `sink-table-linux` (`linux-hw`) and `sink-table-macos` (`macos`), which survey the runner's counters first and, where none is live, mark the measurement step **skipped** with the survey in the summary — never passed. - Registry entry `wai.sink.energy`, SPEC §5 paragraph, CHANGELOG, `browser-sink` §6 cross-reference. ## Measured tables (per completion; 7 rounds; median, range) Each row is one completion of the stated work item, sealed `OnChipCounter` (filtering `Unknown`), `Partial` coverage; the figure is the median of 7 rounds, the declaration covers both the median bracket and the rounds' spread. Figures under different profiles are different scopes and are not compared with each other. **macOS arm64 sink, 32 cores (24 + 8), profile `macos-task`** (`tables/macos-arm64`, taken 2026-10-22 at host load 68 → 22; the per-process counter is not entered by other work). Survey: `macos-task` live; `macos-gpu` **frozen** at 200 ms (the rail republishes in steps), so every GPU row is absent `no_counter`. | capability | path | work item | µJ per completion | range | |---|---|---|---:|---| | `wai.image.png` | software | 1 image 1920×1080 | 39 351 | 37 133 – 43 136 | | `wai.image.jpeg` | software | 1 image 1920×1080 | 35 979 | 34 696 – 37 626 | | `wai.image.avif` | software | 1 image 1280×720 | 77 256 | 75 020 – 83 349 | | `wai.image.jxl` | software | 1 image 1280×720 | 72 071 | 70 471 – 75 429 | | `wai.audio.opus` | software | 10 s | 37 420 | 36 236 – 40 641 | | `wai.audio.flac` | software | 10 s | 15 202 | 14 998 – 16 557 | | `wai.text.zstd` | software | 4 MiB | 5 272 | 5 226 – 5 370 | | `wai.text.xz` | software | 4 MiB | 36 121 | 35 010 – 38 532 | | `wai.video.av1` (WAI's decoder) | software | 30 frames 1280×720 | 2 158 696 | 2 013 449 – 2 696 721 | | `wai.video.int_motion` | software | 8 frames 96×96 | 10 725 | 10 194 – 11 361 | | `wai.neural.int_hyper` | software | 1 image 16×16 | 253 | 249 – 271 | | `wai.neural.int_synth` | software | 1 image 128×128 | 91 693 | 81 547 – 107 889 | | `wai.audio.int_codec` | software | 512 ms | 7 139 | 5 361 – 7 732 | | `wai.world.model` | software | 9 frames 64×64 | 29 017 | 22 585 – 34 155 | Hardware against software through the platform's media tool, 60 frames 1920×1080 per completion, one decoder process per completion (process start included), the hardware path's output MD5 identical to the software path's: | capability | software (µJ) | hardware (µJ) | |---|---:|---:| | `wai.video.avc` | 813 574 (746 208 – 897 657) | 339 273 (317 648 – 396 031) | | `wai.video.hevc` | 993 261 (856 930 – 1 130 816) | 325 831 (282 287 – 361 154) | | `wai.video.av1` | 702 494 (597 035 – 743 501) | 360 325 (331 915 – 375 465) | | `wai.video.vp9` | 657 926 (612 448 – 688 726) | 353 286 (344 104 – 424 094) | The hardware figures are **Partial**: they count the decoding process and the work billed to it (the platform's decoder service), and nothing of the media engine, for which this host has no channel at all (energy-measurement §9.2). They are not a statement that hardware decode costs that little. **Linux x86-64 sink, 32 cores, profile `linux-rapl`** (`tables/linux-x86_64-32-core`, quiet host, package counter readable through its `energy` group). This build has no codec wrappers (no codec development libraries on the host), so the integer decoders only: | capability | work item | µJ per completion | range | |---|---|---:|---| | `wai.video.int_motion` | 8 frames 96×96 | 21 847 | 17 795 – 29 562 | | `wai.neural.int_hyper` | 1 image 16×16 | 740 | 677 – 750 | | `wai.neural.int_synth` | 1 image 128×128 | 467 945 | 416 570 – 473 106 | | `wai.audio.int_codec` | 512 ms | 45 176 | 36 996 – 45 597 | | `wai.world.model` | 9 frames 64×64 | 198 411 | 114 293 – 212 064 | **Linux x86-64 sink, 20 cores, with a discrete GPU, profile `linux-rapl`** (`tables/linux-x86_64-20-core`): taken once the host had been idle for a minute (no CI job, load below 1). Two earlier runs there fell on its own CI jobs and gave every row `unmetered` (`no_marginal`, `below_attribution_floor`): a machine-wide counter read on a busy host refuses its rounds rather than report a low figure. Integer decoders only, as on the 32-core host: | capability | work item | µJ per completion | range | |---|---|---:|---| | `wai.video.int_motion` | 8 frames 96×96 | 23 908 | 22 267 – 26 247 | | `wai.neural.int_hyper` | 1 image 16×16 | 777 | 723 – 840 | | `wai.neural.int_synth` | 1 image 128×128 | 423 233 | 392 325 – 445 587 | | `wai.audio.int_codec` | 512 ms | 46 631 | 44 953 – 49 434 | | `wai.world.model` | 9 frames 64×64 | 226 021 | 219 470 – 233 902 | Its survey reads the GPU's total-energy counter at 13–23 W against the device's own ~10.4 W power reading per 1 s interval: inside the plausibility rule, and read at 1 s, so it gives no rows in a 200 ms table. ## Decisions and why - **Profiles in energy-measurement, records in a new extension.** The profiles (counter, fabrics, attribution, idle baseline, window) are §9 of energy-measurement as the brief asks; the three records are `sink-energy.md`, so the existing receipt families keep their bytes. - **Existing fields, existing arithmetic.** Every figure is written with jwp-receipts §2.4.5's members (`joules_micro`, `energy_provenance`, `energy_uncertainty {relative_ppm, window_us}`, filtering, calibration reference) inside the record's signature, and every hardware figure comes from energy-measurement §6.2's integer forms. A segment's own bracket is *bounded* (it holds exactly that segment's decode), which is §6.2 with `Wn = Wd = 1` and `N = 0`: §2.3's "removes the completion-count term by construction". - **Repeats are in the record.** A row is a figure only when every one of its rounds is a measurement (live rails, ratio ≥ 1, figure ≥ 1, declarable); the median round is sealed, the declaration is `max(p_median, q)` with `q` the rounds' range read as rectangular, and the median's §4 verdict decides `figure` / `relative` / `unmetered (noisy)`. A failed round makes the row unmetered with that round's reason; nothing is dropped. - **Raw readings are published and bound.** Every interval (`[nJ, µs]`) of every bracket is in `readings.json` (RFC 8785 form, the bytes its digest covers); both implementations recompute every round from it. - **The per-task counter on macOS.** Every CPU, media and neural-engine channel of IOReport's `Energy Model` is frozen for an unprivileged reader on the build host; the kernel's per-process energy (with billed work, so a platform decoder service's CPU work counts) is live and is not entered by other work on a shared host, so it gives clean rounds at load 22–74 where a machine-wide counter cannot. - **One hop table.** The session's split uses the stream log's hop roles and its figure / unmetered-marker / absent rule (stream-termination §2, §3.5, open as #457), and a segment receipt is the sink-decode hop's figure for its segment. - **One fabric vocabulary.** `cpu`, `gpu`, `media`, `npu`; attribution `task` (with billed and serviced work) or `rail` (sole user ≥ 99%), as settled with the per-fabric record of #438. A counter every user shares gives no attribution: the figure is the bracket over the whole decode, not a fabric share, and neither it nor a `Partial` figure carries a division by fabric. A hop's figure is restated only for its sole user; a shared hop is the marker `hop_shared`. Nothing is divided by busy time. - **Table method for live segments.** A live sink usually cannot bracket each segment cleanly; its segment receipt can instead take its figure from its own table (`ModelBased`, calibrated on that host) or another key's (`Estimator`), checkable against the table row by integer arithmetic. - **Display.** `estimator-display/1`: the panel's declared on-mode power (its energy label's figure) scaled by the luminance setting and the time on screen; always `Estimator`, with the sink's stated uncertainty, never added to a decode figure. - **No product names in the tables.** The processor is described by architecture and cores; the procedure text names the open-source media tool and its digest for reproduction. - **The table key** is the corpus's fixed-seed key (it names the corpus, not a machine). ## Checks Build host = the arm64 macOS build host through `rb`; Linux = the 32-core x86-64 host (`nice -n 19`), used as a second platform and while the build host's work volume was below rb's free-disk floor. | check | where | result | |---|---|---| | `cargo test --locked --no-default-features --features sink_energy --lib sink_energy::` | build host and Linux, at the head | 9 passed | | `… --lib sink_meter::` | build host (3 incl. the macOS task counter), Linux (2) | passed | | `… --test sink_energy` (corpus byte for byte, every case/reason/table) | build host, Linux (HEAD) | 2 passed | | `wai_sink_energy_vectors verify ../live-conformance/sink-energy` | build host | PASS, PASS | | `wai registry export --check` | build host | up to date | | `cargo test --locked --lib energy_report::` (Nat now crate-visible) | build host | 11 passed | | `cargo test --locked --lib codecs::registry` | build host | 14 passed | | `cargo test --locked --no-default-features --features receipt_claims --lib claim_text` (its gate widened) | build host | 2 passed | | `cargo metadata --locked --offline` (no dependency added; no lockfile changes) | build host | ok | | `cargo clippy --locked --no-default-features --features sink_energy[,codecs,neural_int,video_int,audio_int,world_int] --all-targets`, notes in the new files | build host, Linux | no note in the new files, either feature set, on macOS and on Linux | | `python3 -I live-conformance/sink_energy_verify.py live-conformance/sink-energy` | local | 51 cases, every reason, 3 of 3 tables, 30 of 30 mutations: exit 0 | | `python3 -I live-conformance/sink_energy_verify.py --table <dir>` on each of the three measured tables | local | PASS | | every other `python3 -I` verifier `wai-conformance.yml` runs (31) | local | all exit 0 | | `python3 -I scripts/check-tests-in-ci.py`, `check-workflow-concurrency.sh`, `check-spec-refs.py`, `check-wai-section-refs.py`, `check-no-vanity-metrics.sh`, `test-public-text-checks.sh`, `check-comparative-prose.sh`, `check-orphan-sources.py` | local | all exit 0 | | `check-public-names.sh` on the 28 changed files under `wai/`; commit messages against the patterns | local | clean; 0 hits | | CI on the PR: every run cancelled on opening, as asked; before cancelling, the existing hardware job had failed on the host's missing GPU driver | forge | cancelled (13 runs) | | merges of `origin/main` (`b1681667`, then `1a465f23`; one CHANGELOG conflict, both entries kept): `wfsteps.py HEAD 41c5bafba 761876d38 origin/main`; `ruby -ryaml` over every workflow | local | `0 problem(s)`; all parse | | the procedure itself (`wai_sink_table run`) | macOS build host, both Linux hosts | each table verifies as written, then independently | | corpus generated on Linux = corpus generated on macOS | Linux | byte for byte (`--test sink_energy` on Linux against the macOS-generated file) | The pre-existing `unpinned_read` dead-code warning (`int_payload`) appears with any feature set here and on `main`; not touched. ## Falsifications All committed first; each rule broken alone in a scratch copy, the guarding tests run, the file restored (`git diff --quiet` clean after each). | rule broken (wai-rs) | `--lib sink_energy::` | `--test sink_energy` | |---|---|---| | bracket liveness (`sd_uw > 0`, ≥ 5 live intervals) always true | fails | fails | | `unmetered_as_zero` check removed | passes | fails (cases expecting it) | | §6.4 sole-user rule removed | passes | fails | | median taken as the least round | fails | fails | | readings round comparison removed | passes | fails | | §5.3 chain rule removed | passes | fails | | spread term `q` forced to 0 | fails | fails | | "ModelBased exactly for the sink's own table" removed | passes | passed at first: **no case guarded it**; added "another key's table sealed ModelBased", after which it fails | Python verifier (in a scratch copy beside the original, then removed): the §6.4 sole-user check dropped → exit 1 ("a shared hop stated as the session's: expected split_hop_mismatch, got accept"); the half-up rounding of an interval's power dropped → exit 1 (self-test, and the 32-core table's round 0 recomputes `sd_uw` 477 705 against the stated 477 704). Its own mutation self-test: 30 mutation kinds, every mutated copy fails. CI skip semantics: the survey step's script, run against a survey with no live counter, writes `live=false` and "SKIPPED (not passed)" to the summary, so the measurement step's `if: steps.survey.outputs.live == 'true'` is skipped; against a live one it writes `live=true`. Counters: a scripted counter that holds its value, advances in one lump, advances in 7 of 10 intervals, or fails a read is `frozen` (`sink_meter::tests`); on the real build host the GPU rail reads `frozen` at 200 ms while idle, and those rows are absent `no_counter` rather than sealed. ## Not done / open - **The existing `RAPL and NVML on jetson-hub` job** failed on this PR's first run ("NVML saw no GPU") while that host's GPU driver was not loaded after its restart on 2026-10-24; the host's state, not this change. - **NVML.** On that sink the total-energy counter, read every second, passes the plausibility rule while reading 1.4–1.9× the device's own power reading; read every 200 ms it fails. A plausible counter is read at its own 1 s interval, which a 200 ms table does not use, so no `linux-nvml` rows exist yet. After the host restarted on 2026-10-24 its GPU driver was not loaded, and the survey says `no-counter`. - **No hardware decode path on Linux** is reachable from the procedure: neither Linux host has a media tool installed, and the procedure states `no platform media tool` rather than inventing rows. The GPU rail on macOS is `frozen` at 200 ms, so the hardware rows there have no GPU figure. - **The media engine** has no counter an unprivileged process can read on macOS (no decoder channel exists), so every hardware row is `Partial`. - **The existing reference meters' CPU reader** (`energy_burst::macmon_cpu_power`, used by `wai_meter`, `wai_video_meter`, `wai_world_meter`, `wai_quantum_meter`, `wai_prov`, which seal `OnChipCounter`): on the build host every CPU channel of IOReport's `Energy Model` group is frozen for an unprivileged reader at 100 ms, 500 ms and 1 s, while that reader reports a varying CPU power. Its figure therefore does not come from a CPU energy channel shown live under §3.1 here, and §9.2 now says such a figure is `ModelBased` at best. Not changed in this PR (EN-C's ground). - **Reconciliation with the stacked PRs**, for the lead: the hop table is defined in stream-termination §2/§3.5 (#457) and cited here by name; it belongs in energy-measurement, beside §9 (a section of its own for the hop roles and the figure / marker / absent rule), so both extensions point to one place. The fabric vocabulary and `task`/`rail` match #438's settled terms; this text restates them in §9.1 until #438 lands, when §9.1 should cite §6.3 instead. - **The macOS reader is a second implementation** of #438's fabric meter: wai-rs cannot depend on wai-pack (which depends on wai-rs). Moving one meter into wai-rs for both is follow-up work. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- **Closed:** Landed on main through merge train E (#499, merge bf39ec9a); verified by content: this PR's head is in main
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]>
The sink profiles name the fabrics a reading covers (cpu, gpu, media, neural) and how it is
the sink's: task, the operating system's account of the sink's own processes, or bracket,
the marginal over an idle bracket on a shared counter. No counter is divided among its users
by their busy time; a hop's figure is restated in a session's split only where the session
was its sole user, at least 99% of its busy time, and is otherwise absent as hop_shared.

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 session receipt carries hop entries by the roles a stream log's hop entries use
(1 origin, 2 relay, 3 sink decode, 4 display): a figure with its own class, the unmetered
marker with its reason where the sink knows the hop took place and has no figure, and no
entry where the sink states nothing. A segment receipt is the sink-decode hop's figure for
its segment.

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]>
Settled with the per-fabric record: the fabric term is npu; task is the operating system's
account of the sink's own processes; rail is a sole user's whole rail. A counter every user
shares gives the sink no attribution: its figure is the bracket over the whole decode, not a
fabric's share, and like a Partial figure it carries no division by fabric.

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]>
sink-energy §5: a segment receipt is the sink-decode hop's figure for its segment
Some checks failed
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, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (pull_request) Has been cancelled
energy meters on real hardware / sink decode table · per-task energy and the GPU rail on macOS (pull_request) Has been cancelled
energy meters on real hardware / sink decode table · RAPL on jetson-hub (NVML surveyed) (pull_request) Has been cancelled
b3f4b48fb7
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Taken by wai_sink_table on the host once it had been idle for a minute: the package
counter live, every row a figure.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merge origin/main into wai/energy-sink-metering
Some checks failed
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
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Has been cancelled
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Has been cancelled
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Has been cancelled
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (pull_request) Has been cancelled
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
energy meters on real hardware / sink decode table · RAPL on jetson-hub (NVML surveyed) (pull_request) Has been cancelled
energy meters on real hardware / sink decode table · per-task energy and the GPU rail on macOS (pull_request) Has been cancelled
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (pull_request) Has been cancelled
df8c7f493f
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dcharlot closed this pull request 2026-10-26 20:58:09 -04:00
Some checks failed
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-quantum studio and desktop / does this change touch it (pull_request) Has been cancelled
wai-quantum studio and desktop / studio · browser (pull_request) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (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-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Has been cancelled
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Has been cancelled
WAI HTTP delivery / WAI HTTP · does this change touch it (pull_request) Has been cancelled
WAI HTTP delivery / HTTP corpus · falsify mutations 0/3 (pull_request) Has been cancelled
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 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
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Has been cancelled
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Has been cancelled
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Has been cancelled
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (pull_request) Has been cancelled
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
energy meters on real hardware / sink decode table · RAPL on jetson-hub (NVML surveyed) (pull_request) Has been cancelled
energy meters on real hardware / sink decode table · per-task energy and the GPU rail on macOS (pull_request) Has been cancelled
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Has been cancelled
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Has been cancelled
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (pull_request) Has been cancelled

Pull request closed

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!460
No description provided.