From 8c7669e7f0b71396995fff9b98d88b5e07ae0464 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 30 Oct 2026 16:47:00 -0700 Subject: [PATCH 1/5] quest: plan Delay on subscriptions, JS owed waits, audio reach-back, lite drop-count tails, and an SPS recheck Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- quest/m1/README.md | 2 ++ quest/m1/js-ordered-owed.md | 21 +++++++++++++++++++++ quest/m1/subscription-delay.md | 26 ++++++++++++++++++++++++++ quest/m2/README.md | 3 +++ quest/m2/audio-latest-reach-back.md | 15 +++++++++++++++ quest/m2/lite-tail-drop-count.md | 20 ++++++++++++++++++++ quest/m2/sps-fuzz-h264-reader.md | 16 ++++++++++++++++ 8 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 quest/m1/js-ordered-owed.md create mode 100644 quest/m1/subscription-delay.md create mode 100644 quest/m2/audio-latest-reach-back.md create mode 100644 quest/m2/lite-tail-drop-count.md create mode 100644 quest/m2/sps-fuzz-h264-reader.md diff --git a/AGENTS.md b/AGENTS.md index 6271abeeec6..570a7848973 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,7 +59,7 @@ The API is the most important thing to get right. A bad shape costs a breaking change in every language, and the surface is huge. - Report the public API and wire impact of every change, in the PR description and whenever asked. -- A breaking change marks its title with `!` and its Impact bullet says how to migrate. Only the release cut writes `MIGRATE.md` sections; leave existing Unreleased notes for that cut to consume. +- A breaking change marks its title with `!` and its Impact bullet says how to migrate. Feature PRs add their migration notes under `## Unreleased`; only the release cut writes version sections. - Keep things private until a current or planned consumer needs them. Scrutinize every new exported item. - Never add `foo_with_x`, `foo_checked`, or a compatibility shim. Make the breaking change to `foo` instead. - Let the type system make misuse unrepresentable diff --git a/quest/m1/README.md b/quest/m1/README.md index a6a89d23d21..83bdf92f51c 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -186,6 +186,8 @@ removed). - [moqsrc max-delay](/quest/m1/gst-max-delay.md) - GStreamer's `moqsrc` takes a `max-delay` property instead of a hard-coded 1 s budget - [moq-clock moves into kio::clock](/quest/m1/kio-clock.md) - moq-clock and its test macro become `kio::clock` and `#[kio::test]` before the cut publishes them - [Poll-first async](/quest/m1/poll-first/README.md) - library crates compile no coroutines by default: async wrappers are generated from `poll_*` and gated behind an `async` feature +- [Subscriptions take a Delay { min, max }](/quest/m1/subscription-delay.md) - one Delay type across every subscription API, and omitted max means 0 in every binding +- [JS ordered reads wait only on owed groups](/quest/m1/js-ordered-owed.md) - @moq/net matches Rust's owed-group wait ## Related diff --git a/quest/m1/js-ordered-owed.md b/quest/m1/js-ordered-owed.md new file mode 100644 index 00000000000..8abb5868d41 --- /dev/null +++ b/quest/m1/js-ordered-owed.md @@ -0,0 +1,21 @@ +# [S] JS ordered reads wait only on owed groups + +## Goal + +`@moq/net`'s ordered reader on a subscribed track waits at a hole while the feed may still deliver that group, bounded by its max delay, as Rust's does since [moq-dev/moq#7403](https://github.com/moq-dev/moq/pull/7403). It no longer skips the gap, and local tracks still skip. + +## Plan + +Decided 2026-10-30 (maintainer, follow-up planning): m1, for parity with Rust before the cut. Port #7403's rule rather than invent a JS one: + +- Only subscription-fed copies owe groups. +- An unlimited budget waits until the hole resolves, with no grace period. +- A finite budget gives up on the same media-time and wall-clock rule. + +Port #7403's Rust tests, using JS's manual clock. + +Public API: none. Wire: none. + +## Required + +- [Ordered reads wait only on owed groups](/quest/m2/ordered-owed-wait.md) - its PR, #7403, is the Rust rule this ports diff --git a/quest/m1/subscription-delay.md b/quest/m1/subscription-delay.md new file mode 100644 index 00000000000..f8a457eaee0 --- /dev/null +++ b/quest/m1/subscription-delay.md @@ -0,0 +1,26 @@ +# [L] Subscriptions take a Delay { min, max } + +## Goal + +Every subscription API takes the same `Delay { min, max }` the consumer APIs got in [moq-dev/moq#7311](https://github.com/moq-dev/moq/pull/7311), in place of a bare max delay. That covers moq-net `Subscription`, `@moq/net`'s `Subscription`, FFI `MoqSubscription`, and moq-c `moq_subscription`. The `Delay` type moves to moq-net / `@moq/net`, and an omitted max means 0 in every binding, including Dart, Go, and C. + +## Plan + +Decided 2026-10-30 (maintainer, follow-up planning): m1, because it breaks the public API and doing it before the cut avoids a second break. + +- **Move:** move `Delay` from `moq_mux::container` / `Container.Delay` into `moq_net::track` / `Track.Delay`. Re-export it from moq-mux for one release with a MIGRATE note. +- **Rename:** rename `max_delay` to `delay.max` throughout, about 1,050 Rust and 600 JS references. On the wire, `max` still travels as today and `min` stays local. +- **Semantics:** they're settled in [moq-dev/moq#7403](https://github.com/moq-dev/moq/pull/7403): every default is 0, unlimited is explicit (Rust `None`, JS `null`, FFI null), and an omitted JS field means 0. +- **Binding defaults:** this change also fixes the bindings where an omitted max is still unlimited. + - Dart: an optional nullable parameter. + - Go: a nil `MaxDelay` in a zero `Subscription{}`. + - C: `max_delay_present` is false in a zeroed struct. + + Each one's omitted or zeroed form becomes 0. The breaking binding changes get MIGRATE notes under `## Unreleased`. + +Public API: breaking in every binding. Wire: none. + +## Required + +- [Ordered reads wait only on owed groups](/quest/m2/ordered-owed-wait.md) - its PR, #7403, settles the max-delay defaults and touches the same fields +- [A max-delay decoder joins an earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - its PR, #7311, adds `Delay` diff --git a/quest/m2/README.md b/quest/m2/README.md index 4f4f2452b0a..2f1131953b1 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -272,3 +272,6 @@ lite-07; bitrate enforcement is not part of this release. - [Resume delivers late groups below its resume point](/quest/m2/resume-late-groups.md) - a bonus: groups that arrive late after a route change are delivered instead of written off - [The HLS server follows each path once](/quest/m2/hls-follow-once.md) - `Following` reports synced absence, so moq-hls resolves with one follow - [moq-bench pins the instances it measures](/quest/m2/bench-epoch-pin.md) - pooled requests pin their announced epoch, so a takeover is not counted as the same broadcast +- [Audio's Latest start reaches back](/quest/m2/audio-latest-reach-back.md) - moq_audio's start covers min plus warmup like other consumers +- [moq-lite tails count drops](/quest/m2/lite-tail-drop-count.md) - SUBSCRIBE_DROP accounting replaces the session-tail grace from lite-05 +- [Re-run the SPS fuzzer on h264-reader](/quest/m2/sps-fuzz-h264-reader.md) - the H.264 SPS overflow is rechecked on the new parser diff --git a/quest/m2/audio-latest-reach-back.md b/quest/m2/audio-latest-reach-back.md new file mode 100644 index 00000000000..bd35e077f6d --- /dev/null +++ b/quest/m2/audio-latest-reach-back.md @@ -0,0 +1,15 @@ +# [S] Audio's Latest start reaches back by min plus warmup + +## Goal + +`moq_audio`'s decoder under `Start::Latest` starts at the group covering live edge − (`delay.min` + warmup), as the other consumers do since [moq-dev/moq#7311](https://github.com/moq-dev/moq/pull/7311). Today it floors at the live-edge group and primes its warmup by trimming. + +## Plan + +Decided 2026-10-30 (maintainer, follow-up planning): m2. Use the same start resolution the container consumer uses rather than a second one. With `min` and warmup at 0, the start is unchanged. Add a test of a cold join with a 2-group-old start. + +Public API: none. Wire: none. + +## Required + +- [A max-delay decoder joins an earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - its PR, #7311, adds the start rule diff --git a/quest/m2/lite-tail-drop-count.md b/quest/m2/lite-tail-drop-count.md new file mode 100644 index 00000000000..d72ee0fed33 --- /dev/null +++ b/quest/m2/lite-tail-drop-count.md @@ -0,0 +1,20 @@ +# [M] moq-lite session tails count drops instead of waiting a grace + +## Goal + +From lite-05 on, a moq-lite session's tail is complete once every group up to the end has arrived or been reported in SUBSCRIBE_DROP. The 1 s `ledger::GRACE` (`rs/moq-net/src/ledger.rs:19`) then no longer decides it, in Rust and JS. + +## Plan + +Decided 2026-10-30 (maintainer, follow-up planning): m2. Found by the audit on [moq-dev/moq#7403](https://github.com/moq-dev/moq/pull/7403). The maintainer dislikes grace periods. + +- Count per version. lite-01..04 have no drop message, so they keep today's behaviour. +- Account for holes left by lost datagrams. +- Guard against a peer that never accounts for a group: decide with the maintainer whether that ends the tail with an error or keeps waiting. Don't add a new grace. +- The IETF tail keeps its grace until the moq-transport gap-signal extension (planned in [moq-dev/moq#7425](https://github.com/moq-dev/moq/pull/7425)) lands, since moq-transport can't signal gaps. + +Public API: none. Wire: none. + +## Related + +- [Drop set](/quest/m1/drop-set.md) - the per-cursor drop reporting this counts diff --git a/quest/m2/sps-fuzz-h264-reader.md b/quest/m2/sps-fuzz-h264-reader.md new file mode 100644 index 00000000000..8fb06b343fc --- /dev/null +++ b/quest/m2/sps-fuzz-h264-reader.md @@ -0,0 +1,16 @@ +# [XS] Re-run the SPS fuzzer on h264-reader + +## Goal + +The H.264 SPS fuzz target runs against moq-mux's parser after [moq-dev/moq#7398](https://github.com/moq-dev/moq/pull/7398) moves it onto `h264-reader`. Any overflow or panic it finds there is fixed in moq-mux's validation and recorded in the fuzz-findings SPS quest. + +## Plan + +Decided 2026-10-30 (maintainer, follow-up planning): [moq-dev/moq#7313](https://github.com/moq-dev/moq/pull/7313) found the crop and width overflows in `h264-parser` ([therishidesai/h264-parser#9](https://github.com/therishidesai/h264-parser/issues/9)). #7398 replaces that crate for H.264, so the finding has to be rechecked on the new parser. Run the target long enough to cover the old reproducer and a fresh corpus. If `h264-reader` is clean, narrow the SPS quest to H.265. + +Public API: none. Wire: none. + +## Required + +- [Parse H.264 with maintained crates](/quest/m2/h264-reader.md) - its PR, #7398, moves SPS parsing to h264-reader +- [Media parsers have fuzz targets](/quest/m2/media-fuzz.md) - its PR, #7313, adds the SPS target and the fuzz-findings epic From d9bbb74ef4aeb829b1cd03cbfb91bdead655f0be Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Fri, 30 Oct 2026 16:50:26 -0700 Subject: [PATCH 2/5] quest: Delay.min rides the lite-07 wire; moq-net has no local-only API Co-Authored-By: Claude Opus 5.5 --- quest/m1/lite07-finalize.md | 1 + quest/m1/subscription-delay.md | 5 +++-- rs/moq-net/AGENTS.md | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/quest/m1/lite07-finalize.md b/quest/m1/lite07-finalize.md index 0ce44d7c2ef..290f0ecb2d8 100644 --- a/quest/m1/lite07-finalize.md +++ b/quest/m1/lite07-finalize.md @@ -109,6 +109,7 @@ timestamp bound in [#6168](https://github.com/moq-dev/moq/pull/6168). - [Lite-07 by start](/quest/m1/time-addressing/lite07.md) - lite-07 carries group starts, previous-start links, GROUP_END, and time bounds instead of sequence numbers - [@moq/net by start](/quest/m1/time-addressing/js.md) - JS speaks the same lite-07 wire - [Announcement takeover](/quest/m1/announce-takeover.md) - ANNOUNCE_TAKEOVER preserves a proven instance under a new independent route +- [Subscriptions take a Delay { min, max }](/quest/m1/subscription-delay.md) - lite-07 SUBSCRIBE carries the delay's min as well as its max ## Related diff --git a/quest/m1/subscription-delay.md b/quest/m1/subscription-delay.md index f8a457eaee0..46fae77c43e 100644 --- a/quest/m1/subscription-delay.md +++ b/quest/m1/subscription-delay.md @@ -9,7 +9,8 @@ Every subscription API takes the same `Delay { min, max }` the consumer APIs got Decided 2026-10-30 (maintainer, follow-up planning): m1, because it breaks the public API and doing it before the cut avoids a second break. - **Move:** move `Delay` from `moq_mux::container` / `Container.Delay` into `moq_net::track` / `Track.Delay`. Re-export it from moq-mux for one release with a MIGRATE note. -- **Rename:** rename `max_delay` to `delay.max` throughout, about 1,050 Rust and 600 JS references. On the wire, `max` still travels as today and `min` stays local. +- **Rename:** rename `max_delay` to `delay.max` throughout, about 1,050 Rust and 600 JS references. `max` travels on the wire as today. +- **`min` on the wire:** decided 2026-10-30 (maintainer): because `Delay` lives in moq-net, `min` goes on the lite-07 SUBSCRIBE (and SUBSCRIBE_UPDATE) too, following the rule that everything in moq-net survives the wire. Had `Delay` stayed in moq-mux, it would have stayed local. A relay forwards a subscriber's `min` upstream, and merged subscriptions take the smallest. Nothing reads it on the publisher side yet; later uses could include starting a cold join server-side, delay-tolerant batching, and priority under congestion. Versions that can't carry `min` (lite-01..06, moq-transport) handle it the way moq-net already handles fields an older version lacks. Report what that is in the PR, and bring it to the maintainer if no precedent fits. - **Semantics:** they're settled in [moq-dev/moq#7403](https://github.com/moq-dev/moq/pull/7403): every default is 0, unlimited is explicit (Rust `None`, JS `null`, FFI null), and an omitted JS field means 0. - **Binding defaults:** this change also fixes the bindings where an omitted max is still unlimited. - Dart: an optional nullable parameter. @@ -18,7 +19,7 @@ Decided 2026-10-30 (maintainer, follow-up planning): m1, because it breaks the p Each one's omitted or zeroed form becomes 0. The breaking binding changes get MIGRATE notes under `## Unreleased`. -Public API: breaking in every binding. Wire: none. +Public API: breaking in every binding. Wire: lite-07 SUBSCRIBE and SUBSCRIBE_UPDATE gain `min`. ## Required diff --git a/rs/moq-net/AGENTS.md b/rs/moq-net/AGENTS.md index 41b445931fc..9baac2fd489 100644 --- a/rs/moq-net/AGENTS.md +++ b/rs/moq-net/AGENTS.md @@ -5,6 +5,7 @@ The wire layer. Generic over the transport and media-agnostic: the relay never i - New capabilities are additive SETUP extensions; receivers ignore unknown parameters. - Closes never cascade. Aborting a group must not tear down its track, broadcast, or siblings. - A route change is never fatal on its own. A subscription stays on the instance it started on while that instance still has a route, even after a newer route wins the path. Once none is left (e.g. the publisher's session closed), it ends with an error, never a clean end, and the app requests the path again. Never stitch it onto another instance. A clean end means only a deliberate publisher finish. Players start, stop, and restart on announcements, never on request errors. See "Publisher epochs" in `doc/concept/moq-lite.md`. +- Nothing in moq-net is local-only: every field and behaviour in its API survives the wire, so a subscription, announcement, or end means the same through a relay as in-process. A local-only knob belongs in a crate above moq-net (moq-mux, hang). - Local and remote paths behave the same: a local close, cancel, or error reaches local readers exactly as the remote equivalent would, never as a cleaner end. - The cache is a performance optimization. A relay behaves the same warm or cold: report upstream state (e.g. the largest group), not what the cache happens to hold. - A relay never blocks an answer on other nodes finishing something (replay, catch-up, a full announce set). Report what is known now; a rare undercount is acceptable. From baebaacb2d14bbbe50b7855ac02f145223ffffb5 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 31 Oct 2026 03:05:46 -0700 Subject: [PATCH 3/5] quest: settle the Delay downgrade, unlimited forms, and shim from review - subscription-delay: no moq-mux re-export (AGENTS.md forbids shims); older versions treat `min` as not stated, following carries_max_delay; each binding keeps an explicit unlimited form; label the Rust prereq. - m1: list subscription-delay before Finalize moq-lite-07, which needs it. - audio-latest-reach-back: relate audio-join-budget, which edits the same joining_latest path. Co-Authored-By: Claude Opus 5.5 --- quest/m1/README.md | 2 +- quest/m1/subscription-delay.md | 8 ++++---- quest/m2/audio-latest-reach-back.md | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/quest/m1/README.md b/quest/m1/README.md index 29b79a348bc..122c3c2107c 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -141,6 +141,7 @@ removed). - [Broadcast archives](/quest/m1/archive/README.md) - ANNOUNCE, TRACKS, and a SUBSCRIBE per track record every track under a prefix to any object_store, a host serves a stored broadcast back, and the catalog drops its archive pointer for a per-rendition `hls` field - [HLS recorder target](/quest/m1/hls-record-target.md) - the moq-cli HLS recorder takes its target duration from the declared `hls` duration - [Lite-07 padding datagram](/quest/m1/lite07-padding-datagram.md) - lite-07 defines a padding datagram that receivers discard, so browser probes can pad below media +- [Subscriptions take a Delay { min, max }](/quest/m1/subscription-delay.md) - one Delay type across every subscription API, and omitted max means 0 in every binding - [Finalize moq-lite-07](/quest/m1/lite07-finalize.md) - when the maintainer cuts it, lite-07 negotiates as `moq-lite-07` and the next release ships it - [Moxygen minimal interop](/quest/m1/smoke-moxygen.md) - waiting on Moxygen to send a registered filter type, then the smoke lane stops hiding its failure - [Smoke token subject](/quest/m1/smoke-token-subject.md) - condition: grant subject ships; then the release smoke test signs with `--subject` @@ -186,7 +187,6 @@ removed). - [moqsrc max-delay](/quest/m1/gst-max-delay.md) - GStreamer's `moqsrc` takes a `max-delay` property instead of a hard-coded 1 s budget - [moq-clock moves into kio::clock](/quest/m1/kio-clock.md) - moq-clock and its test macro become `kio::clock` and `#[kio::test]` before the cut publishes them - [Poll-first async](/quest/m1/poll-first/README.md) - library crates compile no coroutines by default: async wrappers are generated from `poll_*` and gated behind an `async` feature -- [Subscriptions take a Delay { min, max }](/quest/m1/subscription-delay.md) - one Delay type across every subscription API, and omitted max means 0 in every binding - [JS ordered reads wait only on owed groups](/quest/m1/js-ordered-owed.md) - @moq/net matches Rust's owed-group wait - [HLS export follows a resolution change](/quest/m1/hls-reinit-same-codec.md) - a decoder-identity change under the same codec string starts a new init and segment tag - [The last AAC segment advertises its full duration](/quest/m1/hls-final-aac-segment.md) - a TS import's final audio segment's EXTINF matches what decodes diff --git a/quest/m1/subscription-delay.md b/quest/m1/subscription-delay.md index 46fae77c43e..0fc9172780c 100644 --- a/quest/m1/subscription-delay.md +++ b/quest/m1/subscription-delay.md @@ -8,20 +8,20 @@ Every subscription API takes the same `Delay { min, max }` the consumer APIs got Decided 2026-10-30 (maintainer, follow-up planning): m1, because it breaks the public API and doing it before the cut avoids a second break. -- **Move:** move `Delay` from `moq_mux::container` / `Container.Delay` into `moq_net::track` / `Track.Delay`. Re-export it from moq-mux for one release with a MIGRATE note. +- **Move:** move `Delay` from `moq_mux::container` / `Container.Delay` into `moq_net::track` / `Track.Delay`. Update moq-mux's callers directly and leave no re-export, since `AGENTS.md` forbids compatibility shims; the MIGRATE note names the new path. - **Rename:** rename `max_delay` to `delay.max` throughout, about 1,050 Rust and 600 JS references. `max` travels on the wire as today. -- **`min` on the wire:** decided 2026-10-30 (maintainer): because `Delay` lives in moq-net, `min` goes on the lite-07 SUBSCRIBE (and SUBSCRIBE_UPDATE) too, following the rule that everything in moq-net survives the wire. Had `Delay` stayed in moq-mux, it would have stayed local. A relay forwards a subscriber's `min` upstream, and merged subscriptions take the smallest. Nothing reads it on the publisher side yet; later uses could include starting a cold join server-side, delay-tolerant batching, and priority under congestion. Versions that can't carry `min` (lite-01..06, moq-transport) handle it the way moq-net already handles fields an older version lacks. Report what that is in the PR, and bring it to the maintainer if no precedent fits. +- **`min` on the wire:** decided 2026-10-30 (maintainer): because `Delay` lives in moq-net, `min` goes on the lite-07 SUBSCRIBE (and SUBSCRIBE_UPDATE) too, following the rule that everything in moq-net survives the wire. Had `Delay` stayed in moq-mux, it would have stayed local. A relay forwards a subscriber's `min` upstream, and merged subscriptions take the smallest. Nothing reads it on the publisher side yet; later uses could include starting a cold join server-side, delay-tolerant batching, and priority under congestion. Versions that can't carry `min` (lite-01..06, moq-transport) follow the `carries_max_delay` precedent (`rs/moq-net/src/lite/subscribe.rs:113`): encoding to them drops `min`, and decoding from them yields "not stated", not 0. A relay forwards a not-stated `min` as not stated, and it doesn't count toward a merged subscription's smallest, so a legacy peer doesn't pull a merged `min` to 0. - **Semantics:** they're settled in [moq-dev/moq#7403](https://github.com/moq-dev/moq/pull/7403): every default is 0, unlimited is explicit (Rust `None`, JS `null`, FFI null), and an omitted JS field means 0. - **Binding defaults:** this change also fixes the bindings where an omitted max is still unlimited. - Dart: an optional nullable parameter. - Go: a nil `MaxDelay` in a zero `Subscription{}`. - C: `max_delay_present` is false in a zeroed struct. - Each one's omitted or zeroed form becomes 0. The breaking binding changes get MIGRATE notes under `## Unreleased`. + Each one's omitted or zeroed form becomes 0, and unlimited stays expressible. Dart defaults the parameter to `Duration.zero`, so an explicit `null` stays unlimited. Go's nil and C's unset `present` can't mean both, so each gains an explicit unlimited flag in the delay; the PR names it. The breaking binding changes get MIGRATE notes under `## Unreleased`. Public API: breaking in every binding. Wire: lite-07 SUBSCRIBE and SUBSCRIBE_UPDATE gain `min`. ## Required -- [Ordered reads wait only on owed groups](/quest/m2/ordered-owed-wait.md) - its PR, #7403, settles the max-delay defaults and touches the same fields +- [Ordered reads wait only on owed groups](/quest/m2/ordered-owed-wait.md) - the Rust rule; its PR, #7403, settles the max-delay defaults and touches the same fields - [A max-delay decoder joins an earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - its PR, #7311, adds `Delay` diff --git a/quest/m2/audio-latest-reach-back.md b/quest/m2/audio-latest-reach-back.md index bd35e077f6d..abe8ec77f0b 100644 --- a/quest/m2/audio-latest-reach-back.md +++ b/quest/m2/audio-latest-reach-back.md @@ -6,10 +6,14 @@ ## Plan -Decided 2026-10-30 (maintainer, follow-up planning): m2. Use the same start resolution the container consumer uses rather than a second one. With `min` and warmup at 0, the start is unchanged. Add a test of a cold join with a 2-group-old start. +Decided 2026-10-30 (maintainer, follow-up planning): m2. Use the same start resolution the container consumer uses rather than a second one. With `min` and warmup at 0, the start is unchanged. Add a test of a cold join with a 2-group-old start. [Audio join budget](/quest/m2/audio-join-budget.md) changes the same `joining_latest` path in `rs/moq-audio/src/decode/consumer.rs`; whichever lands second builds its start on the other's. Public API: none. Wire: none. ## Required - [A max-delay decoder joins an earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - its PR, #7311, adds the start rule + +## Related + +- [Audio join budget](/quest/m2/audio-join-budget.md) - the other `Start::Latest` join fix in `rs/moq-audio/src/decode/consumer.rs` From e284226a57ca1aacf6a9406a790bc56498d279a8 Mon Sep 17 00:00:00 2001 From: quest fixer Date: Sun, 1 Nov 2026 01:51:54 -0800 Subject: [PATCH 4/5] docs: repair references to completed quests --- quest/m1/js-ordered-owed.md | 4 ++-- quest/m1/subscription-delay.md | 5 ++++- quest/m2/audio-latest-reach-back.md | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/quest/m1/js-ordered-owed.md b/quest/m1/js-ordered-owed.md index 8abb5868d41..8feb7a02cbe 100644 --- a/quest/m1/js-ordered-owed.md +++ b/quest/m1/js-ordered-owed.md @@ -16,6 +16,6 @@ Port #7403's Rust tests, using JS's manual clock. Public API: none. Wire: none. -## Required +## Related -- [Ordered reads wait only on owed groups](/quest/m2/ordered-owed-wait.md) - its PR, #7403, is the Rust rule this ports +- [Ordered reads wait only on owed groups](https://github.com/moq-dev/moq/pull/7403) - its PR, #7403, is the Rust rule this ports diff --git a/quest/m1/subscription-delay.md b/quest/m1/subscription-delay.md index 0fc9172780c..c21ece637d6 100644 --- a/quest/m1/subscription-delay.md +++ b/quest/m1/subscription-delay.md @@ -23,5 +23,8 @@ Public API: breaking in every binding. Wire: lite-07 SUBSCRIBE and SUBSCRIBE_UPD ## Required -- [Ordered reads wait only on owed groups](/quest/m2/ordered-owed-wait.md) - the Rust rule; its PR, #7403, settles the max-delay defaults and touches the same fields - [A max-delay decoder joins an earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - its PR, #7311, adds `Delay` + +## Related + +- [Ordered reads wait only on owed groups](https://github.com/moq-dev/moq/pull/7403) - the Rust rule; its PR, #7403, settles the max-delay defaults and touches the same fields diff --git a/quest/m2/audio-latest-reach-back.md b/quest/m2/audio-latest-reach-back.md index abe8ec77f0b..49970cb5631 100644 --- a/quest/m2/audio-latest-reach-back.md +++ b/quest/m2/audio-latest-reach-back.md @@ -6,7 +6,7 @@ ## Plan -Decided 2026-10-30 (maintainer, follow-up planning): m2. Use the same start resolution the container consumer uses rather than a second one. With `min` and warmup at 0, the start is unchanged. Add a test of a cold join with a 2-group-old start. [Audio join budget](/quest/m2/audio-join-budget.md) changes the same `joining_latest` path in `rs/moq-audio/src/decode/consumer.rs`; whichever lands second builds its start on the other's. +Decided 2026-10-30 (maintainer, follow-up planning): m2. Use the same start resolution the container consumer uses rather than a second one. With `min` and warmup at 0, the start is unchanged. Add a test of a cold join with a 2-group-old start. [Audio join budget](https://github.com/moq-dev/moq/pull/7342) changes the same `joining_latest` path in `rs/moq-audio/src/decode/consumer.rs`; whichever lands second builds its start on the other's. Public API: none. Wire: none. @@ -16,4 +16,4 @@ Public API: none. Wire: none. ## Related -- [Audio join budget](/quest/m2/audio-join-budget.md) - the other `Start::Latest` join fix in `rs/moq-audio/src/decode/consumer.rs` +- [Audio join budget](https://github.com/moq-dev/moq/pull/7342) - the other `Start::Latest` join fix in `rs/moq-audio/src/decode/consumer.rs` From a5c5e34f28e9be1857dc2dc82b5fdc2d47c6c9d5 Mon Sep 17 00:00:00 2001 From: quest fixer Date: Sun, 1 Nov 2026 06:11:10 -0800 Subject: [PATCH 5/5] docs: link completed Delay prerequisite to its landed PR --- quest/m1/subscription-delay.md | 5 +---- quest/m2/audio-latest-reach-back.md | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/quest/m1/subscription-delay.md b/quest/m1/subscription-delay.md index c21ece637d6..f83f4370502 100644 --- a/quest/m1/subscription-delay.md +++ b/quest/m1/subscription-delay.md @@ -21,10 +21,7 @@ Decided 2026-10-30 (maintainer, follow-up planning): m1, because it breaks the p Public API: breaking in every binding. Wire: lite-07 SUBSCRIBE and SUBSCRIBE_UPDATE gain `min`. -## Required - -- [A max-delay decoder joins an earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - its PR, #7311, adds `Delay` - ## Related +- [A max-delay decoder joins an earlier group](https://github.com/moq-dev/moq/pull/7311) - its PR, #7311, adds `Delay` - [Ordered reads wait only on owed groups](https://github.com/moq-dev/moq/pull/7403) - the Rust rule; its PR, #7403, settles the max-delay defaults and touches the same fields diff --git a/quest/m2/audio-latest-reach-back.md b/quest/m2/audio-latest-reach-back.md index 49970cb5631..7cef2998013 100644 --- a/quest/m2/audio-latest-reach-back.md +++ b/quest/m2/audio-latest-reach-back.md @@ -10,10 +10,7 @@ Decided 2026-10-30 (maintainer, follow-up planning): m2. Use the same start reso Public API: none. Wire: none. -## Required - -- [A max-delay decoder joins an earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - its PR, #7311, adds the start rule - ## Related +- [A max-delay decoder joins an earlier group](https://github.com/moq-dev/moq/pull/7311) - its PR, #7311, adds the start rule - [Audio join budget](https://github.com/moq-dev/moq/pull/7342) - the other `Start::Latest` join fix in `rs/moq-audio/src/decode/consumer.rs`