The lossy subscriber now states fetch_ms (40): when its layer 1 chain re-anchors at
group 9, it FETCHes the receipts of the groups the anchor skipped (6, 7, 8; 6 the one
its relay dropped, 7 and 8 those refused from the hold). They arrive 40 ms later in
ascending order and each links forward from the one before: history, verified at 525.
Their layers wait for them rather than being refused at the re-anchor: 6's is then
late (unit 6), 7's and 8's in time and applied (units 7, 8).
trace.json states fetch_ms on every subscriber and fetched_ms on every object. The
generator and staged_verify.py model the fetch (moq_settle: a gap per re-anchor,
filled forward or backward, anything else outside the model). Both trace tests feed
fetched receipts through admit_on, require History for each, and compare when each
was fetched; joule-moq's answers the FETCH from the receipts the relay holds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Item 1: Admission::AlreadyAdmitted. A group the chain admitted before, other than
its head, offered again with the same bytes returns it before anything is verified,
never ChainBreak; the head again is Linked as before. Test:
an_admitted_group_offered_again_changes_nothing (group 3 after head 6; a joiner's
anchor delivered again, then history behind it).
- Item 2: the chain keeps each admitted link with a digest of the group's bytes as
carried (unsigned members included), and holds a group as known only when its bytes
equal the held one's. A legacy group's link covers neither its parent nor its
coverage: a variant with an unsigned coverage is now verified, and refused
(Unverified); one with its parent rewritten is the head again and changes nothing.
Test: a_legacy_variant_is_not_the_group_admitted (the review's jmoq_probes2 case).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
jwp-receipts §3 step 3: a group the verifier admitted to the chain before, offered
again with the same bytes (a legacy group's unsigned parent among them), passes and
changes nothing: MOQT can deliver one object twice, by a subscription and a fill
(§3.4). ReceiptChains::admit returns Admission::AlreadyAdmitted ("accept again")
before verifying; the chain keeps each admitted link with the parent it came with,
so a legacy group whose parent differs is not that group and is checked as before.
verify_object therefore succeeds for any admitted group, not only the head, without
verifying the group again. jwp_verify.py does the same.
Two vectors on main change their expected outcome, as the decision requires:
chain_head_predecessor_replayed (b0 again after b1: accept again, was chain_break)
and chain_legacy_replay_loop (g1 replayed with its parent unchanged: accept again,
was chain_break); the rewritten g0 is still refused. New vectors from the review:
chain_duplicate_after_the_head_moved and chain_join_anchor_delivered_again. New test:
an_admitted_group_verifies_its_objects_after_the_head_moved.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ReceiptChains, re-implemented from WAI jwp-receipts §3 step 3 as amended:
- NEW 7: admit verifies each group's key, signature and label first
(ReceiptVerifier::verify_group, any key); a forged group is Unverified and never
held, admitted or anchored, joined or re-anchored.
- NEW 1: each anchor leaves a gap; a group inside it linking to either end is
history, so an ascending FETCH after a re-anchor and a joiner's ascending fill
are admitted (the review's two sequences, as tests).
- NEW 2: held groups indexed by link and by parent; offered again only when a group
they link to is admitted; each signature verified once; a history count per chain
and a total across chains beside the live count; Chain.links capped at
CHAIN_HELD_LINKS (65 536, as WAI's reference). Tests count held groups,
verifications and re-offers, not time.
- OPEN 1: for_moq refuses a count above MAX_HELD_GROUPS; HoldBound::from_json
refuses a statement without counts; admit_on and ObjectReceipt::check_moqt refuse
a receipt off its track, group or object (MoqtMismatch) unheld.
tests/staged_moq_trace.rs drops its pending queue: each subscriber's chains are
for_moq under its stated bound; the joiner subscribes at its join and anchors; the
lossy subscriber's lost receipt is recovered by re-anchoring, its layers refused;
when each receipt is verified or refused, and each status's evidence, are the
trace's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
tests/staged_moq.rs drops its own pending queue: each subscriber's verifier is
ReceiptChains::for_moq under the bound the subscriber states (HoldBound::from_json,
which refuses a statement without counts), joining where it joins; each receipt
goes through admit_on (its key, alias and MOQT group) and check_moqt (its object),
and what was held is settled through take_events. The time each receipt is
verified or refused is compared with the trace's.
moq/trace.json (format /3) states object 0 on each object, a hold bound on each
subscriber, and two new subscribers: one whose relay drops the receipts-track object
of layer 1's group 6 (7 and 8 held under a bound of 2, refused when 9 passes it,
the chain re-anchoring at 9, the layers of 6 to 8 offered with no evidence and
refused, 9 to 11 verified: P-W1 recovered through the MoQ path), and one that joins
after GOP 0 and anchors each chain at its first receipt. The generator and
staged_verify.py model the bounded wait (moq_settle): hold within the count, past
it refuse and re-anchor, refuse what the anchor skips; statuses carry their
evidence. The full and capped subscribers' outcomes are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
moq-streaming-format §4.1 and the check (wai-rs staged::moq, staged_verify.py):
- NEW 3: no two tracks of a catalog carry one wai_receipt_alias or name one
receipts track; a rendition of a layer, a clone among them, overrides both.
delta_clone_layer_2 is now refused (staged-receipts); delta_clone_layer_2_own_chain
overrides both and adds its receipts track, and is accepted.
- NEW 6: the check needs the catalog track's namespace. A checker without it
(check_catalog, check_catalog(c) in Python) refuses a set that mixes declared and
inherited namespaces (staged-malformed). catalogs.json (format /3) states the
namespace each case is checked under, and gains the inheritance cases of the
review: layers inheriting under example/cam1, other/ns and none known; layer 1
twice, declared and inherited; every track inheriting.
- NEW 5: MOQT-22 integers reach 2^64 - 1 (§8.1): MAX_MOQT_VARINT, the Python slack
cap, the CHANGELOG line and the §8.1 citation are gone.
- NIT: two tracks of a catalog MUST NOT share a namespace|name pair.
- P-J5: a subscriber sets no delivery timeout on its subscription to a receipts
track (MOQT §9.20.3, §9.20.4); a receipt it loses anyway it recovers.
- Text for what follows: a receipt says which MOQT object it is about (group_id,
object_id, key and alias; moqt_mismatch), the bound a subscriber states (a count
live, never a time alone; history; total), and gaps filled by an ascending FETCH
or fill as history.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
jwp-receipts §3 step 3. Each anchor leaves a gap between the last group admitted
before it and the earliest at or after it; a group inside the gap that links to
either end is history and moves that end, so a gap fills ascending (as MOQT
delivers a FETCH or a fill), descending, or from both ends, and closes when one
group links to both. A group linking into a gap is never chain_break.
Holding states three counts: live per chain (past it the chain re-anchors),
history per chain for groups inside a gap (past it the lowest held below is
refused, alone), and a total across chains; MAX_HELD_GROUPS caps every hold.
Held groups are indexed by link and by parent; a held group is offered again
only when a group it links to is admitted, so re-offering the head changes and
verifies nothing; each signature is verified once (a group admitted or held is
known by its link hash). ReceiptChains::for_moq refuses a count above the
maximum; HoldBound::from_json refuses a statement without counts; admit_on and
ObjectReceipt::check_moqt refuse a receipt off its track, group or object
(moqt_mismatch) before verifying or holding it.
The reviewer's two sequences are corpus vectors (gap fetched ascending after a
re-anchor; a joiner's ascending fill under a live bound of 4), with the history
bound passed and the total passed across two tracks; jwp_verify.py implements
the same rules; chain_gap_differential.py states the new counts and refetches
ranges ascending.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review items 1 (joule-moq) and 10:
- ReceiptChains: with_hold(HoldBound {max_groups, max_age_ms}), with_join(),
admit() -> Admission (Linked, Anchor, History), expire(), take_events(),
anchors(), held(): a held group is admitted when its parent arrives; past the
bound the chain's held groups are refused (ChainRefusal::ParentNotRecovered)
and it re-anchors at its next group above its head; a joiner anchors at its
first group; groups linking back to a labelled anchor are history. Without the
options accept() behaves as before.
- Tests: a lost receipt stalls without holding and recovers by re-anchoring (by
count and by time), the lost group and the refused ones regained as history,
a fetched parent releasing what waits on it (P-J2); a late joiner admits from
its anchor and back to the chain's start (P-J3).
- wai_dispatch.rs: the capability docs name capabilities and standards only, with
no evaluative or comparative wording. The names and prose checks pass on every
tracked joule-moq file, given by explicit path.
- joule-code-conformance.yml: the C1-C4 pack's summary is matched as
'^[1-9][0-9]* passed, 0 failed, ' ("10 failed" no longer reads as "0 failed";
an empty pack fails).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review item 1 (wai-rs and Python), P-W1:
- jwp_delivery::ReceiptChains: with_hold(HoldBound {max_groups, max_age_ms}) holds
a parent_not_held group and admits it when its parent arrives; holding past the
count, or expire() past the time, refuses every group held for the chain
(parent_not_recovered, a new §3.3 reason) and re-anchors it at its next group
that passes steps 1-2, the freshness bound and a group_id above the head's.
with_join() anchors a chain it holds nothing of at its first such group. Behind
a labelled anchor, groups that link back are admitted as history. admit()
returns the Admission (Linked, Anchor, History); take_events() reports what
became of held groups; anchors() reports every anchor and the group before its
gap. Without the options nothing changes, and every existing vector is
byte-identical.
- jwp-conformance: twelve chain vectors with a recovery input (fetched parent,
release order, lost receipt with and without holding, re-anchor and history
regained, a re-anchor refused behind its head, join mid-stream / at the start /
on a legacy chain / with a stale group), appended after every existing vector;
outcomes accept anchor and accept history. jwp_verify.py implements the same from
the text; chain_gap_differential.py lets both verifiers recover on half its
chains (seeds 1-3, 400 chains each: 0 differ).
- Unit tests: a lost receipt stalls without recovery, re-anchors by count and by
time, and is regained as history; a late joiner admits from its anchor.
- jwp-receipts §3 step 3 History applies behind any labelled anchor; §5.9 lists
the recovery vectors; README documents the input and outcomes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review items 5 (joule-moq), 6 and 9 (P-J4):
- RelayTrackConfig names the track's receipts track; subscribe_tracks takes the
media tracks and the receipts tracks; ingest_receipts takes the receipts track
a bundle arrives on and forwards it only to that track's subscribers. The
trace test subscribes each subscriber to the receipts tracks of its own tracks
and no longer filters what reaches it (it asserts nothing else does).
- RelayStats::receipts_forwarded_for_dropped is keyed by (track, group, object)
and counted when the receipts are sent to the subscriber the object was
dropped for (or when the object is dropped after they were): P-J1a now counts
two identical-bytes objects as two, P-J1b counts none for receipts never
forwarded. Both are unit tests.
- MockRelay::ingest refuses a send time past the caller's clock instead of
saturating it (P-J4, a unit test).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review items 2, 3, 4, 5 (catalog and text), 7, 8 and 9:
- wai_staged is {set, layer, lead_ms}: a set's extent is its tracks, layers 0..max
each with a track, so MSF delta updates and clones change it alone; lead_ms is
read only while the set is live (as MSF §5.2.8 treats targetLatency).
- renderGroup an integer fitting u64, depends an array of strings, a track's
namespace|name unique, one namespace per set, every depends entry a catalog
track in its own namespace (namespace inherited per MSF §5.2.2):
staged-malformed / staged-depends / staged-grouping.
- One receipts track per media track (wai_receipts_track), at its priority,
group g carrying the receipts of media group g; an alias shared only by
renditions of one chain; every wai_receipt_alias in the catalog an integer
compared as a number.
- §4.1 text: recovery by FETCH, a stated hold bound with re-anchoring, joining
(MOQT-22 replaced Joining FETCH with fill fetch streams: cited so); the
conditional form of what a receipts-track subscriber holds; subscriber priority
first (§5.1.2); SUBGROUP_DELIVERY_TIMEOUT from FIN to acknowledgement; no
datagrams (MSF §6); timeouts capped at 2^62−1; a longer sink latency cannot
extend a publisher timeout; a MOQT relay carrying receipts as payload is not a
§4 relay (jwp-receipts §4.1 amended); §6 says what §4.1 paraphrases; CMSF-01
pinned.
- jwp-receipts §3 step 3: holding, re-anchoring, joining, history behind a
labelled anchor, what an anchor does not prove; §3.3 parent_not_recovered;
§6.1 anchors. (Implementation follows.)
- staged::moq rewritten to match; layer_timeout_ms capped at 2^62−1; the corpus
generator's trace check uses checked arithmetic and refuses rather than
panics; catalogs.json gains the reviewed type and namespace cases and the
delta and on-demand cases; staged_verify.py type-checks before use.
catalog_diff.py: 100,000 catalogs, 0 differences, 0 crashes.
- joule-moq StagedTrack drops layers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The rate predictor of wai.audio.int_refine, rdiv(a·num + (b − a)·ρ, num),
beside the f64 linear interpolation of the layered-PCM ladder idea, written
from its description (no earlier code copied): on integer fixtures (a sine by
Bhaskara I's approximation, a triangle, full-scale steps), mono and stereo, at
seven ratios, the SHA-256 of the integer predictions and every sample where the
two differ. They agree at every dyadic phase, where the f64 form is exact (the
generator asserts it); they differ by one, at 1/6 and 147/160 phases, where f64
rounds next to a half.
Written by wai_staged_vectors; staged::tests::rate_matches_ported_reference_where_exact
(design test 15) checks it with its own f64 form; staged_verify.py checks it with
its own integer predictor and IEEE arithmetic.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- staged.rs: StagedTrack (wai_staged), refines_of / layer_from_envelope (the
layer read from the envelope's refines), check_mrl_agrees (compares, never
obeys, the prototype header), live_object_rules_ok.
- receipt.rs: ReceiptChains, WAI jwp-receipts §3 step 3 per (publisher key,
track_alias): head, ParentNotHeld held not accepted, ChainBreak for a second
head or fork, GroupIdNotIncreasing for a legacy group.
- mock.rs: MockRelay forwards staged tracks on a caller-driven clock —
configure_track (track timeout, live one-object groups), subscribe_tracks
(capping by subscription, own timeout, receipts track), ingest (arrival at
this hop, queue time, object timeout), ingest_receipts (no timeout, sent at
once), advance (MOQT §5.2 at this hop), relay_stats (RelayStats: forwarded,
dropped_on_timeout, receipts_forwarded_for_dropped). Nothing sleeps.
- messages.rs and wai_dispatch.rs: max_mrl_layer, mrl_layer and
mrl_total_layers superseded for WAI objects (staged-delivery §13); the
LAYERED_PCM idea is wai.audio.int_refine's rate predictor; its namespace is a
registry decision. lib.rs exports staged, ReceiptChains, ChainRefusal and
drops a comparative sentence.
- tests/staged_moq_trace.rs: staged_tracks_drop_on_timeout_keep_receipts runs
WAI's staged-conformance/moq/trace.json as a third implementation (own
receipts, own chain verifier, own stage rule) and must reach the trace's
fates, verification times, presented stages, misses, statuses and relay
counts; the capped subscriber holds two chains.
- Unit tests for ReceiptChains and the relay's hop timeout boundary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Runs staged-conformance/moq/trace.json with real receipts: the catalog passes
§4.1 and its aliases are the receipts'; each base decodes through
staged::decode_base; the publisher signs one labelled group per object, a chain
per track; the relay applies MOQT §5.2 at its hop, drops what outlives its
timeout, forwards every receipt and signs a delivery-gap claim per dropped
object, checked against its receipt. Each subscriber keeps ReceiptChains, holds
a receipt for its parent (frame 5's) and admits it when the parent arrives,
checks delivered objects over their bytes and dropped ones by inclusion, and
takes each object once it has it and its verified receipt. Its steps, reports,
final units, presented stages, deadline misses and each layer's status must be
the trace's; each base's stage claim seals and verifies; the capped subscriber
holds exactly two chains.
CI: a step in wai-conformance.yml (features staged,video_int,receipt_claims);
joule-code-conformance.yml fires on wai/staged-conformance/** for joule-moq's
run of the same trace.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Receipts-track objects travel on streams of their own and can arrive out of
order; jwp-receipts §3 step 3 admits a group only after its parent. The trace
now verifies each receipt once it and every earlier receipt of its track have
arrived (verified_ms), and a sink applies a layer at the later of its arrival
and that time: frame 5's slow receipt holds frames 6 and 7 until it arrives.
moq-streaming-format §4.1 states the consequence. Both verifiers derive it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wai_staged_vectors writes and verifies moq/: catalog.json (a live set: base
track, two layer tracks, the receipts track), catalogs.json (each §4.1 rule
broken alone, two faults, an unreadable set, two sets) and trace.json: the
envelopes of video_int_motion_gop_perframe on their tracks, one object to a
group, through one relay to a subscriber of every track and one of base and
layer 1. Per-object OBJECT_DELIVERY_TIMEOUT is the slack to the last unit
covered; the relay drops the layer 2 of two GOPs on timeout and forwards their
receipts; frame 5's layer 1 passes the relay within its timeout and is late at
the sink. expect: each object's fate, the sink's steps, reports, final units,
stage presented per clock position, deadline misses, each layer object's
status, and the relay's counts. verify checks it from the committed inputs;
staged_verify.py checks it independently (catalog check, slack, priorities,
relay fates, the steps replayed through its own sink).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
StagedTrack (to_json/from_json), SetTrack::to_catalog_json, check_catalog with
the six ordered reasons of moq-streaming-format §4.1, depends_for,
live_object_rules_ok, priorities_ok (MOQT numbers: lower first),
effective_timeout_ms and expired_at_hop (MOQT §5.2 at one hop),
layer_timeout_ms (slack to the last unit covered, never 0) and
base_timeout_floor_ms. Unit tests, one per rule, and a test that §4.1 lists the
reason tokens in order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
moq-streaming-format, re-read against draft-ietf-moq-transport-22 and
draft-ietf-moq-msf-01 (named in its status note):
- §4.1 Staged tracks: one track per layer; wai_staged {set, layer, layers,
lead_ms}; MSF isLive, renderGroup, depends, altGroup, temporalId/spatialId
rules; a closed, ordered catalog check (staged-malformed, -set-incomplete,
-depends, -grouping, -lead, -receipts); one object per group, its own
subgroup, on live tracks; priority by MOQT's number order (lower is first);
OBJECT_DELIVERY_TIMEOUT no more than the slack to the last unit covered,
per object as an object property; timeouts run per hop and are never the
lateness rule; capping by subscription; the receipts track (wai-receipts).
- §4 gains wai_receipt_alias: MOQT's Track Alias is chosen per session by
each publisher on the path (MOQT §3.1.3), so it is not the track_alias a
receipt signs past the first hop.
- §5: a relay that does not forward an object forwards its receipts; a
delivery-gap claim is not added to the publisher's receipts track.
- Corrections from the re-read: MSF's packaging values (no 'catalog' value),
MSF does not set priorities (MOQT does), MOQT properties and MSF §12.
staged-delivery §8, §9, §13, jwp-receipts §4.4 and http-delivery §10.2 and
Appendix C point at it. Three wire identifiers join the registry.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
build.rs writes the header from the FFI surface on every build; the committed
copy still declared a constant the code no longer has.
Co-Authored-By: Claude Opus 5.5 <[email protected]>