joule-moq-relay: DVR on the relay, seek back by FETCH, and the seek power (L7b) #351

Merged
dcharlot merged 47 commits from joule-moq/dvr into main 2026-10-20 08:29:33 -04:00

2026-10-20

Transaction Science
Merge remote-tracking branch 'origin/main' into joule-moq/dvr
Some checks failed
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Has been cancelled
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (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-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
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (pull_request) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Has been cancelled
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Has been cancelled
# Conflicts:
#	wai/CHANGELOG.md
2026-10-20 08:28:15 -04:00
Transaction Science
live-metering §3.1: the version 2 sentence cites live-recording §8 in prose
All checks were successful
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 26s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m41s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m29s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 10m1s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m16s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 18m47s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 23s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m10s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 4m2s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 22m44s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 6s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m44s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 5s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 6s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 1m16s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 2m6s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 27s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 8m36s
wai-pack / wai-pack · linux (pull_request) Successful in 2m7s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m2s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 3m30s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 19m33s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 14m33s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 19s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 43s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 4m38s
wai-pack / wai-pack · macos (pull_request) Successful in 1m4s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m44s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m30s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 4m50s
The link after the table's [start, end) interval read, to the section
reference check, as one link text reaching back to energy-measurement
§2.7; and "keeps none signs" matched the public-text check's "none signs".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 04:06:13 -04:00
Transaction Science
joule-moq-room: Cargo.lock with the relay's secure-objects dependencies
Some checks failed
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-quantum WebGPU member / member · Metal (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Has been cancelled
wai-quantum CPU vector units / does this change touch it (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Has been cancelled
wai-quantum portable tensor program / does this change touch it (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · CPU, mutants, GPU builds (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · integrated GPU (framework-128gb) (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · Metal and the arm64 CPU (macos) (pull_request) Has been cancelled
wai-quantum tensor-engine member / does this change touch it (pull_request) Has been cancelled
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Has been cancelled
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum matrix-extension member / member · matrix extension, emulated (aarch64 Linux) (pull_request) Has been cancelled
wai-quantum matrix-extension member / member · matrix extension absent (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Has been cancelled
wai-quantum service / service · linux (pull_request) Has been cancelled
wai-quantum service / service · macos (pull_request) Has been cancelled
wai-quantum matrix-extension member / does this change touch it (pull_request) Has been cancelled
wai-quantum service / does this change touch it (pull_request) Has been cancelled
Additions only, at the versions joule-code-rs's lock already holds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 03:51:44 -04:00
Transaction Science
joule-moq-relay: depend on joule-moq's secure-objects feature, for the Key ID reader
The DVR store reads an object's Key ID to know a sealed room; the feature
was on only through the dev-dependency, so the relay built as another
crate's dependency (joule-moq-ingest, the e2e's release binary) did not
compile. tests/sealed_objects.rs now gets it from the normal dependency.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 03:51:15 -04:00
Transaction Science
2026-10-20 03:47:32 -04:00
Transaction Science
joule-moq-relay: a sealed room is never kept in the DVR window
confidentiality §9 (merged with L10) says a relay keeps nothing of an
end-to-end encrypted room's tracks beyond delivery; the DVR store kept
every track its prefix rules matched. The store now knows such a room by
its first object carrying a secure-objects Key ID (or properties that do
not read), never writes that group, deletes what it held of the room (its
events and receipts tracks are in the clear and can be whole first),
drops the room's decisions from decisions.wdl, writes nothing more of it,
and records it in sealed.wdl so a restart keeps refusing it. Moderation
still honours the room's decisions for what the cache serves.

live-recording §8.2 states the rule; confidentiality §9 points to it.
The joule-code changelog's stale "a file per group" now describes the
segments, and the cache's media/receipts pairing is documented.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 03:44:17 -04:00
Transaction Science
2026-10-20 03:44:05 -04:00
Transaction Science
joule-moq-relay: an evicted group moves its partner track's mark too
Some checks failed
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
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (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 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-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
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 1m30s
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-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
A group's receipts are cached before its media, and under load the media's stream can be
taken after later groups, so the budget could evict receipts group g before media group g
had arrived: the pairing found no partner to evict, and media g was then cached and served
without receipts (the DVR by-age test, 8 of 64 runs under load). Evicting group g now moves
the partner track's evicted mark to g as well, so a partner group arriving later is never
served from the cache alone; a DVR-kept track still takes it for the store.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 03:24:07 -04:00
Transaction Science
joule-moq-relay DVR test: the by-age case fetches only after the relay took group 3
Taking group 3 can evict a media and receipts pair from the cache; the test's media and
receipts FETCHes are two requests, so an eviction between them left media without its
receipts ("the receipts of group 2 come with it", 16 of 64 runs under load with logging).
The test now waits until the relay's largest object of both tracks is group 3's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 03:22:22 -04:00
Transaction Science
2026-10-20 03:16:14 -04:00
Transaction Science
joule-moq-relay: evicting a stored group evicts every group at or below the mark
The DVR store evicts the track whose lowest group arrived first; a group's arrival time
can be later than a higher group's (the cache recreates a group's state when it takes a
late group), so the store could evict groups 31 and 32 while group 30 stayed held below
the new mark: a gap in the window (1 of 16 concurrent runs of the window test). Moving a
track's evicted mark now removes every group at or below it, in the track and its
partner, so none is ever held below the mark.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 03:14:24 -04:00

2026-10-19

Transaction Science
joule-moq-web e2e: each run starts from no results
A second run in the same output directory failed its service scenario: the room service's
keygen refuses the key file the first run left.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 22:29:16 -04:00
Transaction Science
joule-moq-relay: a media group and its receipts group leave the cache together
The cache's byte budget evicted oldest first across tracks, and a group's receipts are
cached just before its media, so under pressure a media group could stay cached while its
receipts group was gone: a FETCH then served media no receipts covered (seen in the DVR
test a_window_by_age_empties_as_its_groups_pass_it under concurrent runs). The pair is now
evicted together, as the DVR store already evicts it, and kept together while either is
still arriving on a track the store keeps. Budget::take drops the partner's accounting.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 22:20:35 -04:00
Transaction Science
joule-moq-relay: the DVR window stays one run of groups under load
Groups finish, and are sealed, out of order under load. The store evicted by arrival
across all groups, so a group sealed late could sit below groups already evicted, leaving
gaps in the window (seen as [29, 32, 34, ...] in the window test under concurrent runs).
Each track now evicts its lowest group first (across tracks, the one whose lowest group
arrived first), and the writer refuses a seal below the evicted mark when it writes it,
not only when it was queued.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 22:19:05 -04:00
Transaction Science
joule-moq-relay: a FETCH's evicted run never ends before it starts
local_plan's run of groups the DVR store evicted searched the cache for the next whole
group from the run's first group itself, which the cache had already declined (it was at
or below the cache's own eviction mark), so the run ended at g - 1: an inverted range, and
at group 0 a subtraction overflow that panicked the session's task (seen in the DVR test
a_window_by_age_empties_as_its_groups_pass_it). The search now starts after g and skips
groups the cache evicted. local_plan takes the track's cache rather than the track, so the
two cases are unit tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 21:48:17 -04:00
Transaction Science
joule-moq-relay: a kept track's cache takes a group whose streams finish after a later group was evicted
Under load a group's stream can finish after the cache evicted a later group of its track;
the cache refused it, so the DVR store never got it. For a track the store keeps, the cache
now takes it and eviction drops the evicted group's state alone; coverage is unchanged. The
mux test of a stream that arrives after the close no longer fails when, on a loaded host, the
close arrives before its writes do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 20:38:54 -04:00
Transaction Science
Merge origin/main into joule-moq/dvr
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 20:30:47 -04:00
Transaction Science
joule-moq-relay: each session summary is appended in one write
Two sessions that ended together could interleave a line and its newline, leaving two
summaries on one line of summary.out_path.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 20:26:33 -04:00
Transaction Science
2026-10-19 12:12:22 -04:00
Transaction Science
joule-moq-relay: the DVR store appends groups to per-track segments and syncs them once a second
One durable sync per group file could not keep up with a room's audio groups (fifty a second
per publisher), so the writer fell behind and recent groups were neither cached nor stored.
Segments take ten seconds of a track each; a record a later one replaces is marked and
zeroed; a crash loses at most the last second, and what it leaves cut short is cut off and
counted at start.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 12:11:30 -04:00
Transaction Science
joule-code CHANGELOG: name no browser
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 12:02:33 -04:00
Transaction Science
2026-10-19 12:00:05 -04:00
Transaction Science
2026-10-19 11:56:40 -04:00
Transaction Science
2026-10-19 11:49:00 -04:00
Transaction Science
Merge origin/main into joule-moq/dvr
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:48:32 -04:00
Transaction Science
live_rooms_verify.py and the rooms README: the seek power and §10.6's seek requests
The second implementation allows powers bit 4, states each accepted
grant's seek, and gives every request of seek its outcome by §10.6's
rule from the room's grants as §4.3 checks them; the README describes
the dvr room, the new refusals and the seek section.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:45:51 -04:00
Transaction Science
live-conformance/rooms: regenerate with the dvr room, the seek-power refusals and the seek section
Every existing record and stated outcome is unchanged; each accepted
grant's moqt entry gains its seek, false for every grant before the dvr
room.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:45:51 -04:00
Transaction Science
wai-rs live_room: the seek power, seek_check (§10.6), and the corpus's DVR room and seek cases
power::SEEK is bit 4, which the descriptor's and the grant's field rules
now allow; seek_check refuses seek_not_granted a request that starts in a
group below the live edge under a grant without it. The corpus gains the
accepted dvr room, three refused vectors (a grant with seek its role
lacks; a grant and a role with powers bit 5), each accepted grant's seek,
and the seek section.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:45:51 -04:00
Transaction Science
live-rooms: the seek power, and §10.6 seeking back below a track's live edge
powers gains bit 4 seek; a role's and a grant's powers set none above
bit 4, and a grant's seek stays within its role's (§4.3 step 7). §10.6
fixes the live edge (MOQT §3.1.4) and when a FETCH or a SUBSCRIBE's fill
seeks back, which a relay serves only under a proved grant holding
seek, refusing any other with REQUEST_ERROR UNAUTHORIZED and the reason
phrase seek_not_granted (§11). §12 describes the new corpus content.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:45:51 -04:00
Transaction Science
2026-10-19 11:44:56 -04:00
Transaction Science
live_metering_verify.py and the corpus README: version 2
The second implementation reads WMH1 version 1 or 2 by its own layout,
holds version 2's out and stored figures to the entries, chooses the
bound by the fifth byte (B2(M) for 2, B(M) otherwise), and states each
record's version and a version 2 record's DVR figures. New mutations
cover the stated version, figures and bound, the record's DVR bytes, and
the four new refusals. The README describes the new vectors.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:41:34 -04:00
Transaction Science
live-metering corpus: regenerated with version 2
Five vectors added (dvr_relay accepted; dvr_out_exceeds,
dvr_stored_exceeds, record_unknown_version, dvr_record_too_large
refused); every existing vector's records and receipt are unchanged and
its accepted records gain only their version, 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:41:34 -04:00
Transaction Science
live-metering corpus: a version 2 relay record and its refusals
dvr_relay (accepted): the lesson's relay keeps a ten-second DVR window of
the composite and signs a version 2 record of it, a viewer joining at
20 s filled from the window. Refused: dvr_out_exceeds and
dvr_stored_exceeds (record_invalid), record_unknown_version (version 3),
dvr_record_too_large (past B2(M) by less than an entry). Each accepted
record's facts state its version, and a version 2 record's six DVR
figures; every existing record keeps its bytes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:41:34 -04:00
Transaction Science
wai-rs live_metering: read and write WMH1 version 2
HopRecord gains dvr: Option<DvrFigures>; Some is version 2, None version 1,
whose bytes are unchanged. A record reads as version 1 or 2 by its own
layout, any other version unknown_version; version 2's out figures are
held to the out entries and its stored figures to the in entries
(record_invalid). The size bound is chosen by the fifth byte before
anything else is read: B2(M) = B(M) + 48 for version 2, B(M) otherwise.
DvrFigures::byte_seconds floors the sum once. energy_steering's corpus
builds its records as version 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:41:34 -04:00
Transaction Science
live-metering: WMH1 version 2, a hop's DVR figures
Version 2 of the per-hop session record adds one field, dvr, after ledger
and before sig: six u64 of what a hop that keeps a DVR window served from
it, stored in it, held in it (byte-seconds, rounded down) and dropped from
it for not verifying against their receipts. A reader reads version 1 or
2 by its own layout; version 2's out and stored figures are held to the
out and in entries (record_invalid); its bound is B2(M) = 600 + 180(M+2),
chosen by the fifth byte before anything else is read. The units do not
sum the DVR figures; the outcome states each record's version and, for
version 2, its figures. The receipt stays version 1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:41:34 -04:00
Transaction Science
joule-moq-relay: the DVR tests
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 11:38:44 -04:00
Transaction Science
2026-10-19 11:35:46 -04:00