From 64383f368ef637789c707f17a64b55e25386e9ea Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 7 Oct 2026 11:26:42 -0700 Subject: [PATCH 1/6] quest: plan five follow-ups from the quest-spawn round Replay history (archive), @moq/publish catalog restart (m0 epoch line), dropped uring session closes, draft-20 FETCH against moxygen, and pipelining the track-info request with the first FETCH. Co-Authored-By: Claude Opus 5.5 --- quest/m0/broadcast-epoch/README.md | 1 + .../publish-catalog-restart.md | 23 +++++++++ quest/m1/README.md | 3 ++ quest/m1/archive/README.md | 1 + quest/m1/archive/replay-catalog.md | 4 ++ quest/m1/archive/replay-history.md | 31 ++++++++++++ quest/m1/ietf-fetch-moxygen.md | 20 ++++++++ quest/m1/pipeline-fetch-info.md | 48 +++++++++++++++++++ quest/m1/uring-drop-close.md | 24 ++++++++++ 9 files changed, 155 insertions(+) create mode 100644 quest/m0/broadcast-epoch/publish-catalog-restart.md create mode 100644 quest/m1/archive/replay-history.md create mode 100644 quest/m1/ietf-fetch-moxygen.md create mode 100644 quest/m1/pipeline-fetch-info.md create mode 100644 quest/m1/uring-drop-close.md diff --git a/quest/m0/broadcast-epoch/README.md b/quest/m0/broadcast-epoch/README.md index b6bc106787..e69e084988 100644 --- a/quest/m0/broadcast-epoch/README.md +++ b/quest/m0/broadcast-epoch/README.md @@ -69,6 +69,7 @@ timeout, and killing the newest epoch falls back to a still-live older one. ## Required - [Apps](/quest/m0/broadcast-epoch/apps.md) - moq-cli, the browser publish and watch components, and demo/web restart into a new epoch and reset on the switch +- [Publish catalog restart](/quest/m0/broadcast-epoch/publish-catalog-restart.md) - `@moq/publish` never reuses catalog group numbers under one name and epoch after a re-announce - [Gateways](/quest/m0/broadcast-epoch/gateways.md) - RTMP, SRT, and WHIP ingest mint an epoch per incoming connection, so an encoder reconnect is a clean takeover - [TS restart](/quest/m0/broadcast-epoch/ts-restart.md) - a signalled backward TS discontinuity finishes the broadcast and continues the same input under a fresh epoch - [Bindings](/quest/m0/broadcast-epoch/bindings.md) - moq-ffi and every wrapper expose the epoch and let a publisher announce one diff --git a/quest/m0/broadcast-epoch/publish-catalog-restart.md b/quest/m0/broadcast-epoch/publish-catalog-restart.md new file mode 100644 index 0000000000..69cb061899 --- /dev/null +++ b/quest/m0/broadcast-epoch/publish-catalog-restart.md @@ -0,0 +1,23 @@ +# [S] @moq/publish never reuses catalog group numbers under one epoch + +## Goal + +`@moq/publish` never publishes different content under one name and epoch. +After an unannounce and re-announce within one run, its catalog track either +continues its group sequence or the broadcast goes out under a fresh epoch, so +a viewer or relay never mistakes a restarted catalog for the old one. + +## Plan + +Found by #4970 (broadcast-epoch apps): `@moq/publish` re-creates its catalog +track after an unannounce and re-announce, so catalog group numbers may +restart under the same name and epoch. That breaks the rule that a broadcast, +track, or group name always means the same content. + +Decided 2026-10-07: verify it first with a test that unannounces and +re-announces a running publish and reads the catalog's group sequence. Fix it +only if it reproduces: continue the sequence, or mint a fresh epoch per +announce, whichever matches how the rest of `@moq/publish` treats a +re-announce. + +Public API: none expected. Wire: none. diff --git a/quest/m1/README.md b/quest/m1/README.md index 1ea8e567a7..8392dc01ac 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -65,6 +65,7 @@ blocks. The quests that gated m0 lines moved under them. - [Dogfood hosted worklets](/quest/m1/dogfood-assets.md) - moq.dev and the moq.pro dashboard host the worklets and call `assets()` after the release - [More tests under load](/quest/m1/test-flakes-2/README.md) - the second round of load-only failures, one quest per flake, fixed at the cause - [moq-uring tests under load](/quest/m1/uring-tests-under-load.md) - uring tests pass while parallel checks share locked memory +- [Dropped uring session closes](/quest/m1/uring-drop-close.md) - a moq-uring session dropped without close() closes its connection - [Media audio-tone check](/quest/m1/media-audio-tone.md) - the media lane's audio-tone check passes under load, fixed at its cause - [CI runner stalls](/quest/m1/ci-runner-stalls.md) - the 0.4 to 0.8 s freezes of both interop tracks on CI are attributed from a week of nightlies and fixed or told apart from playback bugs - [Wire compatibility](/quest/m1/wire-compat.md) - a nightly run tests this checkout against the last published release for tokens, session wire, and catalog/container @@ -79,6 +80,8 @@ blocks. The quests that gated m0 lines moved under them. - [Data consumer timestamps](/quest/m1/data-consumer-timestamps.md) - json and binary consumers return each value's timestamp, in Rust and every binding; snapshots add `latest()` beside an in-order `next()` - [JS data consumer timestamps](/quest/m1/js-data-consumer-timestamps.md) - @moq/json and @moq/flate consumers return each value's timestamp, with snapshot `next()` and `latest()` - [Draft-20 FETCH](/quest/m1/ietf-fetch-location.md) - a draft-20+ FETCH within one group is served from its LOCATION_FILTER, as older drafts are +- [Pipelined first FETCH](/quest/m1/pipeline-fetch-info.md) - a fetch-only reader's first FETCH goes out with the track-info request, on lite and moq-transport +- [FETCH against moxygen](/quest/m1/ietf-fetch-moxygen.md) - interop covers draft-20 FETCH against moxygen both ways - [FETCH_OK properties](/quest/m1/fetch-ok-properties.md) - our FETCH_OK carries the track properties SUBSCRIBE_OK does, as draft 16+ requires - [Fetch without SUBSCRIBE](/quest/m1/ietf-fetch-only.md) - a relay fetches from an IETF upstream without subscribing, finished tracks included, with End of Track always reported - [IETF object gaps](/quest/m1/ietf-object-gaps.md) - a gapped object ID is refused loudly like a subgroup in Rust and JS, and an overflowing one closes the session in Rust diff --git a/quest/m1/archive/README.md b/quest/m1/archive/README.md index feb8ad71e6..66b0ab5f81 100644 --- a/quest/m1/archive/README.md +++ b/quest/m1/archive/README.md @@ -159,6 +159,7 @@ owned by that prerequisite, not duplicated in archive storage. - [Timelines declare their segment duration](/quest/m1/archive/declared-duration.md) - each timeline entry declares its segment duration (reported or estimated by the publisher), replacing the root `durationMax` - [JS per-track timelines](/quest/m1/archive/js-timelines.md) - `@moq/hang` publishes the same per-track timelines as Rust (it already reads them) - [Fixed HLS target duration](/quest/m1/archive/hls-target.md) - one `EXT-X-TARGETDURATION` for the run, from the reference timeline's declared duration; an overrun is listed with a warning +- [Replay history](/quest/m1/archive/replay-history.md) - replay mode lists a recording from its first segment by reading stored timeline groups, not only the restated tail - [Replay catalog](/quest/m1/archive/replay-catalog.md) - `moq_archive::Reader` republishes the recorded catalog live with `store` set, so stock `moq export hls` serves the whole replay of `moq import archive` - [Idle flush](/quest/m1/archive/flush.md) - idle tracks are recorded within a bounded wall-clock delay, and `flush()` forces a track's pending record out - [Backward timestamps](/quest/m1/archive/backward-timestamps.md) - a resumed recording refuses a restarted source instead of dropping its groups or overlapping media time diff --git a/quest/m1/archive/replay-catalog.md b/quest/m1/archive/replay-catalog.md index 51faba1ca7..a53966404a 100644 --- a/quest/m1/archive/replay-catalog.md +++ b/quest/m1/archive/replay-catalog.md @@ -46,3 +46,7 @@ serve example. Cover a DVR recording whose catalog outlived its first video segment, and fail loudly on a recording with no catalog. + +## Required + +- [Replay history](/quest/m1/archive/replay-history.md) - replay mode lists a recording from its start, which "lists the whole recording" needs diff --git a/quest/m1/archive/replay-history.md b/quest/m1/archive/replay-history.md new file mode 100644 index 0000000000..3e9f721db1 --- /dev/null +++ b/quest/m1/archive/replay-history.md @@ -0,0 +1,31 @@ +# [M] Replay lists a recording from its start + +## Goal + +An HLS export in replay mode lists a recording from its first segment, +however long it is. Today it starts from what the timeline restates when the +exporter joins: at most 256 records (`CHECKPOINT_RECORDS` in `moq-mux`'s +`timeline.rs`), so a recording longer than about 8.5 minutes of 2 s segments +lists from its middle. + +## Plan + +Found by #4978 (bounded HLS playlists), which kept the full listing behind +`moq_hls::export::Config::replay` and confirmed the gap with a 4-record limit: +12 replayed segments listed from `EXT-X-MEDIA-SEQUENCE:8`. The archive test +missed it because its hand-built timeline repeats every record. + +Decided 2026-10-07: read the stored timeline groups in replay mode rather than +only the restated tail, so the exporter or the archive reader replays the +history it holds. Pick where it lives (the exporter fetching past timeline +groups, or `moq_archive::Reader` restating the whole stored timeline) by what +keeps the live path bounded: live joins must keep reading at most the window. + +Test with more records than one checkpoint and assert replay mode lists +segment 0. + +Public API: none expected. Wire: none. + +## Related + +- [Bounded HLS playlists](/quest/m1/hls-bounded.md) - the live renderer rules and the replay mode switch diff --git a/quest/m1/ietf-fetch-moxygen.md b/quest/m1/ietf-fetch-moxygen.md new file mode 100644 index 0000000000..0c1eb0677e --- /dev/null +++ b/quest/m1/ietf-fetch-moxygen.md @@ -0,0 +1,20 @@ +# [S] Draft-20 FETCH interoperates with moxygen + +## Goal + +Interop covers IETF FETCH on draft 20 and later against moxygen in both +directions: moxygen fetching from our relay or publisher, and our relay +fetching from moxygen. Each failure found is fixed at its cause or filed as +its own quest. + +## Plan + +Found by #4971 (draft-20 FETCH): nothing tested here exercises IETF FETCH +against another implementation, and the in-tree interop matrix has no +external peers. + +Decided 2026-10-07: run it through the community interop runner that already +pairs moq with moxygen, adding FETCH cases if it has none. Cover a fetch +within one group, a joining fetch, and a refused multi-group range. + +Public API: none. Wire: none expected. diff --git a/quest/m1/pipeline-fetch-info.md b/quest/m1/pipeline-fetch-info.md new file mode 100644 index 0000000000..36b9ec11d2 --- /dev/null +++ b/quest/m1/pipeline-fetch-info.md @@ -0,0 +1,48 @@ +# [L] The first FETCH does not wait for the track's info + +## Goal + +A fetch-only reader's first FETCH goes upstream together with the request for +the track's info, on moq-lite and moq-transport, removing a round trip per hop. +The fetched group is still handed out only once that route's info is known +and passes the origin's consistency check. + +## Plan + +Decided 2026-10-07, while landing #4974 (fetch-only IETF demand), which keeps +a sequential TRACK_STATUS before the first FETCH. Start after #4974 lands. + +Why the round trip exists (facts, 2026-10-07): nothing in a FETCH request +needs the info. Two serial gates do: + +- The origin accepts the logical track with the first copy's info (timescale, + max_age) and refuses later copies whose info differs + (`model/front.rs` `track_info`), because a relayed group's raw timestamps go + downstream in the group's own timescale while downstream decodes them with + the logical track's advertised one. A fetch is routed only to a copy already + spliced (`resume::Fetching`, `TrackIo::splice`). +- Both sessions register their fetch handler only after their info exchange: + lite's `TrackServeRun` runs TRACK_INFO first because FETCH frames are + timestamped in its units; IETF takes the timescale only from SUBSCRIBE_OK or + TRACK_STATUS_OK. + +What needs the info is decoding the response and approving the group, so: + +- Origin: let `resume::Fetching` send the copy-level fetch to the copy whose + info is in flight (a staged generation), resolving only once that copy is + spliced. On a refused or mismatched info, or a detach, drop the pending + fetch and re-issue on the next route, as failover already does. Never hand + out a group before the info check passes. +- lite: register the fetch handler up front and run FETCH alongside + TRACK_INFO, leaving the response unread in the QUIC stream until the info + lands (which also covers lite-07 untimed framing). On a failed TRACK_INFO, + reject the fetch and reset the stream. +- moq-transport: send TRACK_STATUS alongside the FETCH and take timestamps' + units from it (or FETCH_OK properties where present). + +Tests: a fetch-only reader's FETCH is on the wire before the info answer; a +refused info drops the fetch and retries the next route; a group is never +released before its route's info passes. Measure the first-fetch latency +before and after. + +Public API: none. Wire: none (ordering only). diff --git a/quest/m1/uring-drop-close.md b/quest/m1/uring-drop-close.md new file mode 100644 index 0000000000..b0030f61b3 --- /dev/null +++ b/quest/m1/uring-drop-close.md @@ -0,0 +1,24 @@ +# [S] A dropped moq-uring session closes its connection + +## Goal + +A `moq-uring` `Session` dropped without `close()` closes its QUIC connection +when its last handle goes, instead of living until the idle timeout, which +never fires while the peer keeps the connection alive. + +## Plan + +Found by #4965 (WebTransport close backends), which made the HTTP/3 close task +keep the session alive structurally until its capsule is sent. + +Decided 2026-10-07: first check whether moq-net always calls `close()` before +dropping a session; if it does, the leak only affects direct moq-uring users, +which still need it fixed. Close with a generic application code on drop of +the last handle, matching what the other backends do, with a test that drops +a session and observes the peer's close. + +Public API: none expected. Wire: none. + +## Related + +- [moq-uring tests under load](/quest/m1/uring-tests-under-load.md) - the same crate's test isolation From 5b55d9c6c408fba68fd0eae786cd25b0c5bc5bab Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 7 Oct 2026 11:38:29 -0700 Subject: [PATCH 2/6] quest: address review accuracy nits on the five follow-ups Co-Authored-By: Claude Opus 5.5 --- quest/m0/broadcast-epoch/publish-catalog-restart.md | 4 +++- quest/m1/archive/replay-history.md | 7 ++----- quest/m1/ietf-fetch-moxygen.md | 4 ++++ quest/m1/pipeline-fetch-info.md | 7 ++++--- quest/m1/uring-drop-close.md | 3 ++- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/quest/m0/broadcast-epoch/publish-catalog-restart.md b/quest/m0/broadcast-epoch/publish-catalog-restart.md index 69cb061899..60bff880fb 100644 --- a/quest/m0/broadcast-epoch/publish-catalog-restart.md +++ b/quest/m0/broadcast-epoch/publish-catalog-restart.md @@ -18,6 +18,8 @@ Decided 2026-10-07: verify it first with a test that unannounces and re-announces a running publish and reads the catalog's group sequence. Fix it only if it reproduces: continue the sequence, or mint a fresh epoch per announce, whichever matches how the rest of `@moq/publish` treats a -re-announce. +re-announce. `js/publish/src/broadcast.ts` mints the epoch once and keeps the +publisher across unannounce and announce on purpose, which points at +continuing the sequence. Public API: none expected. Wire: none. diff --git a/quest/m1/archive/replay-history.md b/quest/m1/archive/replay-history.md index 3e9f721db1..f4df95a514 100644 --- a/quest/m1/archive/replay-history.md +++ b/quest/m1/archive/replay-history.md @@ -13,7 +13,8 @@ lists from its middle. Found by #4978 (bounded HLS playlists), which kept the full listing behind `moq_hls::export::Config::replay` and confirmed the gap with a 4-record limit: 12 replayed segments listed from `EXT-X-MEDIA-SEQUENCE:8`. The archive test -missed it because its hand-built timeline repeats every record. +missed it because its hand-built timeline repeats every record. Start after +#4978 lands. Decided 2026-10-07: read the stored timeline groups in replay mode rather than only the restated tail, so the exporter or the archive reader replays the @@ -25,7 +26,3 @@ Test with more records than one checkpoint and assert replay mode lists segment 0. Public API: none expected. Wire: none. - -## Related - -- [Bounded HLS playlists](/quest/m1/hls-bounded.md) - the live renderer rules and the replay mode switch diff --git a/quest/m1/ietf-fetch-moxygen.md b/quest/m1/ietf-fetch-moxygen.md index 0c1eb0677e..3338b0ee44 100644 --- a/quest/m1/ietf-fetch-moxygen.md +++ b/quest/m1/ietf-fetch-moxygen.md @@ -18,3 +18,7 @@ pairs moq with moxygen, adding FETCH cases if it has none. Cover a fetch within one group, a joining fetch, and a refused multi-group range. Public API: none. Wire: none expected. + +## Related + +- [Runner approval](/quest/m3/interop-runner-approval.md) - adding FETCH cases to moq-interop-runner needs it diff --git a/quest/m1/pipeline-fetch-info.md b/quest/m1/pipeline-fetch-info.md index 36b9ec11d2..28eeb54f20 100644 --- a/quest/m1/pipeline-fetch-info.md +++ b/quest/m1/pipeline-fetch-info.md @@ -16,15 +16,16 @@ Why the round trip exists (facts, 2026-10-07): nothing in a FETCH request needs the info. Two serial gates do: - The origin accepts the logical track with the first copy's info (timescale, - max_age) and refuses later copies whose info differs + max_age, priority) and refuses later copies whose info differs (`model/front.rs` `track_info`), because a relayed group's raw timestamps go downstream in the group's own timescale while downstream decodes them with the logical track's advertised one. A fetch is routed only to a copy already spliced (`resume::Fetching`, `TrackIo::splice`). - Both sessions register their fetch handler only after their info exchange: lite's `TrackServeRun` runs TRACK_INFO first because FETCH frames are - timestamped in its units; IETF takes the timescale only from SUBSCRIBE_OK or - TRACK_STATUS_OK. + timestamped in its units (only on wires with a track stream; older lite + serves with default info and has no gate to remove); IETF takes the + timescale only from SUBSCRIBE_OK or TRACK_STATUS_OK. What needs the info is decoding the response and approving the group, so: diff --git a/quest/m1/uring-drop-close.md b/quest/m1/uring-drop-close.md index b0030f61b3..9b7669892c 100644 --- a/quest/m1/uring-drop-close.md +++ b/quest/m1/uring-drop-close.md @@ -12,7 +12,8 @@ Found by #4965 (WebTransport close backends), which made the HTTP/3 close task keep the session alive structurally until its capsule is sent. Decided 2026-10-07: first check whether moq-net always calls `close()` before -dropping a session; if it does, the leak only affects direct moq-uring users, +dropping a session (the `Connection` doc comment in +`rs/moq-uring/src/quic/noq/connection.rs` says its session machine does); if it does, the leak only affects direct moq-uring users, which still need it fixed. Close with a generic application code on drop of the last handle, matching what the other backends do, with a test that drops a session and observes the peer's close. From f101eae1a3c915b9544c42d1da6b8856b688a9ce Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 7 Oct 2026 12:16:57 -0700 Subject: [PATCH 3/6] quest: rewrap uring-drop-close Co-Authored-By: Claude Opus 5.5 --- quest/m1/uring-drop-close.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/quest/m1/uring-drop-close.md b/quest/m1/uring-drop-close.md index 9b7669892c..aad4fac727 100644 --- a/quest/m1/uring-drop-close.md +++ b/quest/m1/uring-drop-close.md @@ -13,8 +13,9 @@ keep the session alive structurally until its capsule is sent. Decided 2026-10-07: first check whether moq-net always calls `close()` before dropping a session (the `Connection` doc comment in -`rs/moq-uring/src/quic/noq/connection.rs` says its session machine does); if it does, the leak only affects direct moq-uring users, -which still need it fixed. Close with a generic application code on drop of +`rs/moq-uring/src/quic/noq/connection.rs` says its session machine does); if +it does, the leak only affects direct moq-uring users, which still need it +fixed. Close with a generic application code on drop of the last handle, matching what the other backends do, with a test that drops a session and observes the peer's close. From 98607bf716346c44ebf114bdae97f0994cf4f920 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 7 Oct 2026 13:32:42 -0700 Subject: [PATCH 4/6] quest: test draft-20 FETCH against moxygen locally The maintainer chose a local moxygen run over adding cases to moq-interop-runner, so the runner approval stays Related only. Co-Authored-By: Claude Opus 5.5 --- quest/m1/ietf-fetch-moxygen.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/quest/m1/ietf-fetch-moxygen.md b/quest/m1/ietf-fetch-moxygen.md index 3338b0ee44..1a3aebdb52 100644 --- a/quest/m1/ietf-fetch-moxygen.md +++ b/quest/m1/ietf-fetch-moxygen.md @@ -1,4 +1,4 @@ -# [S] Draft-20 FETCH interoperates with moxygen +# [M] Draft-20 FETCH interoperates with moxygen ## Goal @@ -13,12 +13,15 @@ Found by #4971 (draft-20 FETCH): nothing tested here exercises IETF FETCH against another implementation, and the in-tree interop matrix has no external peers. -Decided 2026-10-07: run it through the community interop runner that already -pairs moq with moxygen, adding FETCH cases if it has none. Cover a fetch -within one group, a joining fetch, and a refused multi-group range. +Decided 2026-10-07: run moxygen locally against this checkout, with no change +to moq-interop-runner. A just recipe or a cell in the in-tree interop harness +(`test/interop`) starts a moxygen relay or client next to ours; pick whichever +fits how moxygen is built or pulled (a pinned container image avoids building +it from source). Cover a fetch within one group, a joining fetch, and a +refused multi-group range. Wire it into CI, at least nightly. Public API: none. Wire: none expected. ## Related -- [Runner approval](/quest/m3/interop-runner-approval.md) - adding FETCH cases to moq-interop-runner needs it +- [Runner approval](/quest/m3/interop-runner-approval.md) - not needed here; only a later move of these cases into moq-interop-runner would wait on it From fe392f750eead84fa11ff416a5fea599b99334f4 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 7 Oct 2026 13:43:27 -0700 Subject: [PATCH 5/6] quest: drop the moxygen FETCH quest; the smoke repo covers moxygen Co-Authored-By: Claude Opus 5.5 --- quest/m1/README.md | 1 - quest/m1/ietf-fetch-moxygen.md | 27 --------------------------- 2 files changed, 28 deletions(-) delete mode 100644 quest/m1/ietf-fetch-moxygen.md diff --git a/quest/m1/README.md b/quest/m1/README.md index bf24cd351b..d45e68b287 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -81,7 +81,6 @@ blocks. The quests that gated m0 lines moved under them. - [JS data consumer timestamps](/quest/m1/js-data-consumer-timestamps.md) - @moq/json and @moq/flate consumers return each value's timestamp, with snapshot `next()` and `latest()` - [Draft-20 FETCH](/quest/m1/ietf-fetch-location.md) - a draft-20+ FETCH within one group is served from its LOCATION_FILTER, as older drafts are - [Pipelined first FETCH](/quest/m1/pipeline-fetch-info.md) - a fetch-only reader's first FETCH goes out with the track-info request, on lite and moq-transport -- [FETCH against moxygen](/quest/m1/ietf-fetch-moxygen.md) - interop covers draft-20 FETCH against moxygen both ways - [FETCH_OK properties](/quest/m1/fetch-ok-properties.md) - our FETCH_OK carries the track properties SUBSCRIBE_OK does, as draft 16+ requires - [Fetch without SUBSCRIBE](/quest/m1/ietf-fetch-only.md) - a relay fetches from an IETF upstream without subscribing, finished tracks included, with End of Track always reported - [IETF object gaps](/quest/m1/ietf-object-gaps.md) - a gapped object ID is refused loudly like a subgroup in Rust and JS, and an overflowing one closes the session in Rust diff --git a/quest/m1/ietf-fetch-moxygen.md b/quest/m1/ietf-fetch-moxygen.md deleted file mode 100644 index 1a3aebdb52..0000000000 --- a/quest/m1/ietf-fetch-moxygen.md +++ /dev/null @@ -1,27 +0,0 @@ -# [M] Draft-20 FETCH interoperates with moxygen - -## Goal - -Interop covers IETF FETCH on draft 20 and later against moxygen in both -directions: moxygen fetching from our relay or publisher, and our relay -fetching from moxygen. Each failure found is fixed at its cause or filed as -its own quest. - -## Plan - -Found by #4971 (draft-20 FETCH): nothing tested here exercises IETF FETCH -against another implementation, and the in-tree interop matrix has no -external peers. - -Decided 2026-10-07: run moxygen locally against this checkout, with no change -to moq-interop-runner. A just recipe or a cell in the in-tree interop harness -(`test/interop`) starts a moxygen relay or client next to ours; pick whichever -fits how moxygen is built or pulled (a pinned container image avoids building -it from source). Cover a fetch within one group, a joining fetch, and a -refused multi-group range. Wire it into CI, at least nightly. - -Public API: none. Wire: none expected. - -## Related - -- [Runner approval](/quest/m3/interop-runner-approval.md) - not needed here; only a later move of these cases into moq-interop-runner would wait on it From 4c696626e197a6b74a9521c35de34f5cbd179294 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Wed, 7 Oct 2026 16:53:48 -0700 Subject: [PATCH 6/6] quest(archive): call #4978's flag history mode --- quest/m1/archive/README.md | 2 +- quest/m1/archive/replay-catalog.md | 2 +- quest/m1/archive/replay-history.md | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/quest/m1/archive/README.md b/quest/m1/archive/README.md index 66b0ab5f81..d7598d69fb 100644 --- a/quest/m1/archive/README.md +++ b/quest/m1/archive/README.md @@ -159,7 +159,7 @@ owned by that prerequisite, not duplicated in archive storage. - [Timelines declare their segment duration](/quest/m1/archive/declared-duration.md) - each timeline entry declares its segment duration (reported or estimated by the publisher), replacing the root `durationMax` - [JS per-track timelines](/quest/m1/archive/js-timelines.md) - `@moq/hang` publishes the same per-track timelines as Rust (it already reads them) - [Fixed HLS target duration](/quest/m1/archive/hls-target.md) - one `EXT-X-TARGETDURATION` for the run, from the reference timeline's declared duration; an overrun is listed with a warning -- [Replay history](/quest/m1/archive/replay-history.md) - replay mode lists a recording from its first segment by reading stored timeline groups, not only the restated tail +- [History from the start](/quest/m1/archive/replay-history.md) - history mode lists a recording from its first segment by reading stored timeline groups, not only the restated tail - [Replay catalog](/quest/m1/archive/replay-catalog.md) - `moq_archive::Reader` republishes the recorded catalog live with `store` set, so stock `moq export hls` serves the whole replay of `moq import archive` - [Idle flush](/quest/m1/archive/flush.md) - idle tracks are recorded within a bounded wall-clock delay, and `flush()` forces a track's pending record out - [Backward timestamps](/quest/m1/archive/backward-timestamps.md) - a resumed recording refuses a restarted source instead of dropping its groups or overlapping media time diff --git a/quest/m1/archive/replay-catalog.md b/quest/m1/archive/replay-catalog.md index a53966404a..58dd9d062d 100644 --- a/quest/m1/archive/replay-catalog.md +++ b/quest/m1/archive/replay-catalog.md @@ -49,4 +49,4 @@ loudly on a recording with no catalog. ## Required -- [Replay history](/quest/m1/archive/replay-history.md) - replay mode lists a recording from its start, which "lists the whole recording" needs +- [History from the start](/quest/m1/archive/replay-history.md) - history mode lists a recording from its start, which "lists the whole recording" needs diff --git a/quest/m1/archive/replay-history.md b/quest/m1/archive/replay-history.md index f4df95a514..06f923e201 100644 --- a/quest/m1/archive/replay-history.md +++ b/quest/m1/archive/replay-history.md @@ -1,8 +1,8 @@ -# [M] Replay lists a recording from its start +# [M] History mode lists a recording from its start ## Goal -An HLS export in replay mode lists a recording from its first segment, +An HLS export in history mode lists a recording from its first segment, however long it is. Today it starts from what the timeline restates when the exporter joins: at most 256 records (`CHECKPOINT_RECORDS` in `moq-mux`'s `timeline.rs`), so a recording longer than about 8.5 minutes of 2 s segments @@ -11,18 +11,18 @@ lists from its middle. ## Plan Found by #4978 (bounded HLS playlists), which kept the full listing behind -`moq_hls::export::Config::replay` and confirmed the gap with a 4-record limit: +`moq_hls::export::Config::history` and confirmed the gap with a 4-record limit: 12 replayed segments listed from `EXT-X-MEDIA-SEQUENCE:8`. The archive test missed it because its hand-built timeline repeats every record. Start after #4978 lands. -Decided 2026-10-07: read the stored timeline groups in replay mode rather than -only the restated tail, so the exporter or the archive reader replays the -history it holds. Pick where it lives (the exporter fetching past timeline +Decided 2026-10-07: read the stored timeline groups in history mode rather +than only the restated tail, so the exporter or the archive reader replays +the history it holds. Pick where it lives (the exporter fetching past timeline groups, or `moq_archive::Reader` restating the whole stored timeline) by what keeps the live path bounded: live joins must keep reading at most the window. -Test with more records than one checkpoint and assert replay mode lists +Test with more records than one checkpoint and assert history mode lists segment 0. Public API: none expected. Wire: none.