From 8f50825d05cf94231765ec0a2805aaf25cdd2ba1 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sun, 25 Oct 2026 21:38:43 -0700 Subject: [PATCH 1/4] quest: plan follow-ups from the 2026-10-25 quest-complete run Co-Authored-By: Claude Opus 5.5 --- quest/m0/nightly/README.md | 1 + quest/m0/nightly/competing-cubic.md | 17 +++++++++++++++++ quest/m1/api-audit/README.md | 1 + quest/m1/api-audit/dial-credential.md | 17 +++++++++++++++++ quest/m1/drop-set.md | 9 +++++++++ quest/m1/js-api-reference.md | 4 ++++ quest/m1/pure-crypto/README.md | 1 + quest/m1/pure-crypto/graviola-cpu-check.md | 16 ++++++++++++++++ quest/m1/test-flakes-2/README.md | 2 ++ quest/m1/test-flakes-2/gst-plugin-load.md | 17 +++++++++++++++++ quest/m1/test-flakes-2/swapped-js-opus.md | 16 ++++++++++++++++ quest/m1/ts-reliability/README.md | 2 +- quest/m1/ts-reliability/export-si-order.md | 9 ++++++--- quest/m1/unbounded-groups/byte-holes.md | 7 ++++++- quest/m2/README.md | 1 + quest/m2/e2ee/README.md | 4 ++++ quest/m2/mpeg2-open-gop-export.md | 18 ++++++++++++++++++ quest/m2/p2p/README.md | 4 ++++ 18 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 quest/m0/nightly/competing-cubic.md create mode 100644 quest/m1/api-audit/dial-credential.md create mode 100644 quest/m1/pure-crypto/graviola-cpu-check.md create mode 100644 quest/m1/test-flakes-2/gst-plugin-load.md create mode 100644 quest/m1/test-flakes-2/swapped-js-opus.md create mode 100644 quest/m2/mpeg2-open-gop-export.md diff --git a/quest/m0/nightly/README.md b/quest/m0/nightly/README.md index 7e0d9638066..c76d9fb0929 100644 --- a/quest/m0/nightly/README.md +++ b/quest/m0/nightly/README.md @@ -37,3 +37,4 @@ Public API: none. Wire: none. - [OBS audio-candidate test](/quest/m0/nightly/obs-audio-candidate.md) - the failed audio candidate check passes under TSan and load, fixed at its reproduced cause - [moq-net builds without async](/quest/m0/nightly/net-async-free.md) - the no-default-features lane stops pulling `async` back in - [Feature shapes per crate](/quest/m0/nightly/hls-poll-closed-gate.md) - moq-hls `Broadcaster::poll_closed` is gated on `server`, and the gate checks each crate's feature shapes in isolation +- [Probe against Cubic](/quest/m0/nightly/competing-cubic.md) - the `competing_cubic` probe test stops resending a stream after its reset diff --git a/quest/m0/nightly/competing-cubic.md b/quest/m0/nightly/competing-cubic.md new file mode 100644 index 00000000000..d1dea8a1a24 --- /dev/null +++ b/quest/m0/nightly/competing-cubic.md @@ -0,0 +1,17 @@ +# [S] Probe against Cubic passes + +## Goal + +`tests::probe::competing_cubic` in `rs/moq-quic/src/tests/probe.rs`, run by +Nightly through `just rs quic-sim`, passes on `main`, fixed at its cause. + +## Plan + +[moq-dev/moq#6719](https://github.com/moq-dev/moq/pull/6719) found it failing +the same way on `main` and on its branch: a probe resends a stream after that +stream was reset. A reset stream's data is never retransmitted, so find which +path still queues it (probe padding, a loss-recovery retransmit, or a stale +send queue entry) and stop it there. The other probe tests in the release +suite pass, so the fix must keep them passing. + +Public API: none. Wire: none. diff --git a/quest/m1/api-audit/README.md b/quest/m1/api-audit/README.md index f1e5976e555..70464eaa864 100644 --- a/quest/m1/api-audit/README.md +++ b/quest/m1/api-audit/README.md @@ -80,6 +80,7 @@ Rejected in planning: - [One lifecycle vocabulary](/quest/m1/api-audit/lifecycle.md) - finish, abort, closed, and close mean one thing on every handle, and Rust and JS observers fire alike - [moq-net names say one thing](/quest/m1/api-audit/net-names.md) - demand says `wanted`, colliding stats and transport names are renamed, and Rust and JS info lookups mirror - [Request handlers are named requests](/quest/m1/api-audit/requests.md) - `dynamic()` and `Dynamic` become `requests()` and `Requests` at every layer +- [One meaning for Credential](/quest/m1/api-audit/dial-credential.md) - the internal dial type gets its own name in JS and moq-tokio, and the `Presented` alias goes ## Related diff --git a/quest/m1/api-audit/dial-credential.md b/quest/m1/api-audit/dial-credential.md new file mode 100644 index 00000000000..13bb972b7fc --- /dev/null +++ b/quest/m1/api-audit/dial-credential.md @@ -0,0 +1,17 @@ +# [XS] One meaning for Credential + +## Goal + +`Credential` means only the public `auth::Credential`. The internal type +for what one dial presents, in `js/net/src/connection/tokens.ts` and moq-tokio's +`connect/token.rs`, gets its own name, and the `Presented` import alias in +`tokens.ts` goes away. + +## Plan + +[moq-dev/moq#6751](https://github.com/moq-dev/moq/pull/6751) renamed the +public `Auth` to `Credential`, which collides with these internal types. +Pick a name by role (such as `Presentation`) and use it in both languages. +Both types are internal, so nothing public changes. + +Public API: none. Wire: none. diff --git a/quest/m1/drop-set.md b/quest/m1/drop-set.md index 0d273d2d834..ff9f5b5b063 100644 --- a/quest/m1/drop-set.md +++ b/quest/m1/drop-set.md @@ -236,6 +236,15 @@ run array once for the track and once per sink (`track.ts:914`). The replacement keeps the per-cursor walk incremental in both languages, with a benchmark swept over runs and readers. +### Idle START unreport test + +[moq-dev/moq#6717](https://github.com/moq-dev/moq/pull/6717) made the JS lite +publisher unreport stale skips below an idle SUBSCRIBE_START, so a later +widening can re-offer them as Rust does, but landed it without a test: the +case needs an update to arrive between START and the first group. Cover it +deterministically, through the track hooks rather than wire timing, when the +drop runs are reworked here. + ## Required - [A widened max age re-offers cached groups](/quest/m1/resolved-demand.md) - the re-offer path this extends to reported groups diff --git a/quest/m1/js-api-reference.md b/quest/m1/js-api-reference.md index 3f1643554e8..6dda392612e 100644 --- a/quest/m1/js-api-reference.md +++ b/quest/m1/js-api-reference.md @@ -23,6 +23,10 @@ Decided 2026-10-23 (maintainer interview): matches the guides beside it. Nothing generated is committed. - JSR keeps publishing its own reference; this does not replace it. - Rust is a non-goal: docs.rs covers it. +- The link resolver (`nameableReference` from + [moq-dev/moq#6794](https://github.com/moq-dev/moq/pull/6794)) gets fixtures + for a hidden and an exported declaration sharing a name, and for chains of + aliases. ## Required diff --git a/quest/m1/pure-crypto/README.md b/quest/m1/pure-crypto/README.md index de948835495..a730ed081c1 100644 --- a/quest/m1/pure-crypto/README.md +++ b/quest/m1/pure-crypto/README.md @@ -38,3 +38,4 @@ picks a random one, and jsonwebtoken 11 already takes a custom - [ACME on graviola](/quest/m1/pure-crypto/acme-upstream.md) - graviola builds turn ACME back on once rustls-acme gains a provider hook or instant-acme 0.9 is released - [rustls-graviola's QUIC keys](/quest/m1/pure-crypto/graviola-quic.md) - a rustls-graviola release ships QUIC keys, and moq-quic's adapter is deleted - [The next graviola release](/quest/m1/pure-crypto/graviola-release.md) - moq pins a graviola with ECDSA key generation and faster ChaCha20-Poly1305, drops RustCrypto's `p256`, and re-measures the ChaCha20 gap +- [graviola's own CPU check](/quest/m1/pure-crypto/graviola-cpu-check.md) - waiting on ctz/graviola#227; the relay then calls graviola's check instead of mirroring its list diff --git a/quest/m1/pure-crypto/graviola-cpu-check.md b/quest/m1/pure-crypto/graviola-cpu-check.md new file mode 100644 index 00000000000..c504cde51f9 --- /dev/null +++ b/quest/m1/pure-crypto/graviola-cpu-check.md @@ -0,0 +1,16 @@ +# [XS] graviola's own CPU check + +## Goal + +Once a graviola release ships a public CPU feature check +([ctz/graviola#227](https://github.com/ctz/graviola/issues/227)), moq-relay +calls it at startup instead of mirroring graviola's mandatory feature list. +Check the issue and graviola's changelog; until it ships, this quest waits. + +## Plan + +[moq-relay on graviola](/quest/m1/pure-crypto/relay-graviola.md) mirrors the +list because no public check exists. If the check ships before that quest +lands, that quest uses it and this one is deleted. + +Public API: none. Wire: none. diff --git a/quest/m1/test-flakes-2/README.md b/quest/m1/test-flakes-2/README.md index a7cdb7669ac..af1f03de45b 100644 --- a/quest/m1/test-flakes-2/README.md +++ b/quest/m1/test-flakes-2/README.md @@ -46,3 +46,5 @@ Public API: none. Wire: none. - [moq-uring allocations under load](/quest/m1/test-flakes-2/uring-alloc-load.md) - the steady-state send test and MEMLOCK users pass in a parallel check - [IETF22 fallback](/quest/m1/test-flakes-2/ietf22-fallback.md) - an IETF22 browser viewer follows a fallback within the media lane's bound, keeping its session and its audio - [Echo report test on mocked time](/quest/m1/test-flakes-2/export-echo-time.md) - the moq-cli echo report test stops taking 35 s of wall time +- [Swapped Rust to JS Opus](/quest/m1/test-flakes-2/swapped-js-opus.md) - the `rust-swapped -> js` interop cell stops timing out under load +- [Interop loads moq-gst](/quest/m1/test-flakes-2/gst-plugin-load.md) - the interop gst cells always find the local plugin diff --git a/quest/m1/test-flakes-2/gst-plugin-load.md b/quest/m1/test-flakes-2/gst-plugin-load.md new file mode 100644 index 00000000000..a5d6fb382bb --- /dev/null +++ b/quest/m1/test-flakes-2/gst-plugin-load.md @@ -0,0 +1,17 @@ +# [S] Interop loads the local moq-gst plugin + +## Goal + +The interop `gst` cells find the locally built moq-gst plugin on every run, +instead of sometimes failing with "moqsrc not exposed" (the check near +`test/interop/interop.sh:780`), fixed at its cause. + +## Plan + +Seen in [moq-dev/moq#6745](https://github.com/moq-dev/moq/pull/6745)'s +`just test interop --all` runs: one of two runs failed to load the plugin, and +the other passed. Find whether the plugin build, the registry cache +(`GST_REGISTRY`), or `GST_PLUGIN_PATH` races between cells, and make the +harness load the plugin deterministically. + +Public API: none. Wire: none. diff --git a/quest/m1/test-flakes-2/swapped-js-opus.md b/quest/m1/test-flakes-2/swapped-js-opus.md new file mode 100644 index 00000000000..ef0eb042a2c --- /dev/null +++ b/quest/m1/test-flakes-2/swapped-js-opus.md @@ -0,0 +1,16 @@ +# [S] Swapped Rust publisher to JS Opus in interop + +## Goal + +The `rust-swapped -> js` interop cell stops timing out on its Opus check +under local load, fixed at its cause. + +## Plan + +Seen in [moq-dev/moq#6745](https://github.com/moq-dev/moq/pull/6745)'s +`just test interop --all` runs: one of two runs timed out on this cell, and +the other passed. Reproduce under a loaded machine and find whether the JS +subscriber never receives the Opus track, receives it late, or decodes it and +misses the check. + +Public API: none. Wire: none. diff --git a/quest/m1/ts-reliability/README.md b/quest/m1/ts-reliability/README.md index 09f5351f16c..e57b61e455f 100644 --- a/quest/m1/ts-reliability/README.md +++ b/quest/m1/ts-reliability/README.md @@ -100,7 +100,7 @@ catalog linger (#5305). - [PAT changes](/quest/m1/ts-reliability/pat-lifecycle.md) - a revised PAT never leaves a broadcast meaning another program - [TS workflow passes](/quest/m1/ts-reliability/validation-green.md) - the scheduled TS lanes pass on main again, with no lane loosened -- [Export SI order](/quest/m1/ts-reliability/export-si-order.md) - a TS export sends SI after its PMT and a PCR interval, so a hop keeps first emissions +- [Export SI order](/quest/m1/ts-reliability/export-si-order.md) - a TS export sends SI and media after its PMT and a PCR interval, so a hop keeps first emissions ## Related diff --git a/quest/m1/ts-reliability/export-si-order.md b/quest/m1/ts-reliability/export-si-order.md index 2c952959ee9..0e6ddefcfd8 100644 --- a/quest/m1/ts-reliability/export-si-order.md +++ b/quest/m1/ts-reliability/export-si-order.md @@ -1,9 +1,9 @@ -# [S] TS export sends SI after its PMT and PCR +# [S] TS export leads with its PMT and PCR ## Goal -A TS export holds its first SI tables until the PMT and one PCR interval are -out, so an export-to-import hop keeps each table's first emission instead of +A TS export holds its first SI tables and media until the PMT and one PCR +interval are out, so an export-to-import hop keeps each table's first emission instead of dropping it until the table repeats. ## Plan @@ -16,6 +16,9 @@ here is what `si.rs` needs to place a section: one forward PCR step of at most `MAX_INTERVAL`, not the importer's two-second catalog window. Test it with an export-to-import round trip that sees every SI table's first emission. +The first media PES also waits for that PCR, so a reader of our own export +never sees media it cannot place. + Not planned (2026-10-25, maintainer): bounding SI aging against a source that claims a huge mux rate through valid PCR steps. That needs a program-clock floor per input byte, a new contract. diff --git a/quest/m1/unbounded-groups/byte-holes.md b/quest/m1/unbounded-groups/byte-holes.md index 40516ecb018..85e843e3650 100644 --- a/quest/m1/unbounded-groups/byte-holes.md +++ b/quest/m1/unbounded-groups/byte-holes.md @@ -31,10 +31,15 @@ See the [epic](/quest/m1/unbounded-groups/README.md) for the decisions. - Update `drafts/draft-lcurley-moq-lite.md` and run `just drafts check`. Also drop the 32 MiB interop cap in `drafts/draft-lcurley-moq-e2ee.md` if it no longer follows from a model limit. +- Rust readers get a chunked payload read, so a frame larger than memory + never has to be collected whole. [moq-dev/moq#6628](https://github.com/moq-dev/moq/pull/6628) + kept its segments internal; the JS half is in + [JS](/quest/m1/unbounded-groups/js.md). - Test a frame several times the pool size through a relay chain: a late joiner reads it whole, and memory stays near the pool size. -Public API: `Position` gains a byte field, and `MAX_CACHE_BYTES` is removed. +Public API: `Position` gains a byte field, frames gain a chunked read, and +`MAX_CACHE_BYTES` is removed. Wire: a byte offset on the next lite version's range ends and group header. ## Required diff --git a/quest/m2/README.md b/quest/m2/README.md index a7bac8f3b3c..2866b68ed51 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -237,3 +237,4 @@ lite-07; bitrate enforcement is not part of this release. - [No MOQ_* knobs outside the binaries](/quest/m2/env-knobs.md) - test, bench, and library knobs leave the `MOQ_*` prefix - [Clean ALSA playback onset](/quest/m2/audio-alsa-onset.md) - no onset restarts or clicks over PulseAudio - [Interop audit](/quest/m2/audit-interop.md) - our interop runner entries match what we ship, and every red interop cell with a moq.dev side is fixed, reported, or noted as down or unsupported +- [Open-GOP MPEG-2 export](/quest/m2/mpeg2-open-gop-export.md) - a TS export resuming in an open GOP trims the B-pictures that reference the previous one diff --git a/quest/m2/e2ee/README.md b/quest/m2/e2ee/README.md index 0ca06bc4194..6fa23b6d5ae 100644 --- a/quest/m2/e2ee/README.md +++ b/quest/m2/e2ee/README.md @@ -57,6 +57,10 @@ Rejected: The broadcast wrappers merged in [#5876](https://github.com/moq-dev/moq/pull/5876) with `reserve_track`, which still serves declared names; [rust-publish](/quest/m2/e2ee/rust-publish.md) needs nothing more (maintainer, 2026-10-19). +Before `@moq/e2ee` is first published, add its entrypoints to the API +Extractor declaration checks, which analyze only published packages since +[moq-dev/moq#6794](https://github.com/moq-dev/moq/pull/6794). + ## Required - [Undeclared track names](/quest/m2/e2ee/undeclared-names.md) - an encrypted diff --git a/quest/m2/mpeg2-open-gop-export.md b/quest/m2/mpeg2-open-gop-export.md new file mode 100644 index 00000000000..bc2c62e2655 --- /dev/null +++ b/quest/m2/mpeg2-open-gop-export.md @@ -0,0 +1,18 @@ +# [S] Open-GOP MPEG-2 export trims broken-link pictures + +## Goal + +When a TS export starts or resumes at an MPEG-2 sequence header in an open +GOP, it drops or flags the leading B-pictures that reference the previous +GOP, as it already does for codecs whose frames carry presentation time. + +## Plan + +Found while landing [moq-dev/moq#6252](https://github.com/moq-dev/moq/pull/6252): +whole-PES MPEG-2 frames carry their placement time, not their presentation +time, so the leading-picture check cannot apply. Parse the picture coding type +and temporal reference, or the GOP header's `closed_gop` and `broken_link` +flags, to find them. Low priority: decoders usually skip broken-link +B-pictures. + +Public API: none. Wire: none. diff --git a/quest/m2/p2p/README.md b/quest/m2/p2p/README.md index fcd23b43442..66e17539243 100644 --- a/quest/m2/p2p/README.md +++ b/quest/m2/p2p/README.md @@ -150,6 +150,10 @@ Backend choice does not change the policy that watching tabs never re-serve. - Symmetric NATs on both ends fail ICE without TURN. By design the relay keeps serving. +Before `@moq/p2p` is first published, add its entrypoints to the API +Extractor declaration checks, which analyze only published packages since +[moq-dev/moq#6794](https://github.com/moq-dev/moq/pull/6794). + ## Required - [Peer grants](/quest/m2/p2p/peer-grant.md) - the relay issues a key-bound, asymmetrically signed grant a browser can verify; HS256 keys issue none From 96f0cabed4d1a5e485fac0f24ed7ad3e57f9c435 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sun, 25 Oct 2026 21:48:13 -0700 Subject: [PATCH 2/4] quest: move the C follow-ups to c-retire and moq-audio's grant Co-Authored-By: Claude Opus 5.5 --- ...848-follow-the-bandwidth-grant-in-moq-audio-instead-of.md | 5 +++++ quest/m2/release/c-retire.md | 3 +++ 2 files changed, 8 insertions(+) diff --git a/quest/m2/2848-follow-the-bandwidth-grant-in-moq-audio-instead-of.md b/quest/m2/2848-follow-the-bandwidth-grant-in-moq-audio-instead-of.md index f63fd9a454d..c227daf271f 100644 --- a/quest/m2/2848-follow-the-bandwidth-grant-in-moq-audio-instead-of.md +++ b/quest/m2/2848-follow-the-bandwidth-grant-in-moq-audio-instead-of.md @@ -57,6 +57,11 @@ grant; `Options::bandwidth` documents that reservation before video sees a bit and audio is squeezed only once the link cannot carry audio alone. Worth doing for that tail, not worth blocking on. +- moq-ffi's audio encode takes an options record shaped like the video one + ([moq-dev/moq#6781](https://github.com/moq-dev/moq/pull/6781) left + `moq_audio_encode_options` out), so bindings pass the bandwidth policy the + same way for both. The generated C picks it up from moq-ffi. + Decided 2026-10-08: moved to m2, since that tail is the only gain. Tests: retain the shared `Control` tests; an Opus Producer whose diff --git a/quest/m2/release/c-retire.md b/quest/m2/release/c-retire.md index 1c02587a92f..a3824fd6c4d 100644 --- a/quest/m2/release/c-retire.md +++ b/quest/m2/release/c-retire.md @@ -13,6 +13,9 @@ served it are deleted once the generated `moq-c` publishes. The generated - Decided 2026-10-20 (maintainer): no final hand-written notice release, because the hand-written `moq-c` never published to a registry; the generated package is the first `moq-c` users install. +- `rs/moq-c/native-libs/` serves the generated package too, and + `cpp/moq/cmake/moq-ffi.cmake` serves both C++ and C. Move them to a neutral + home shared by both packages before deleting the crate. - The code-free `rs/libmoq` stub left under the old name is [Retire the libmoq stub](/quest/m2/release/libmoq-retire.md)'s, separately. From cb99b00f8ebf37972660b8ec8b4b05ce40ab223f Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sun, 25 Oct 2026 22:52:11 -0700 Subject: [PATCH 3/4] chore: claim competing Cubic probe quest Co-Authored-By: Codex From ae23b3c4f056b9b648538b18cf4a04ce68097293 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sun, 25 Oct 2026 23:02:45 -0700 Subject: [PATCH 4/4] fix(quic): keep completion out of probe reset history Co-Authored-By: Codex --- quest/m0/nightly/competing-cubic.md | 21 +++++++++++++++------ rs/moq-quic/src/tests/probe.rs | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/quest/m0/nightly/competing-cubic.md b/quest/m0/nightly/competing-cubic.md index d1dea8a1a24..e89f5482948 100644 --- a/quest/m0/nightly/competing-cubic.md +++ b/quest/m0/nightly/competing-cubic.md @@ -7,11 +7,20 @@ Nightly through `just rs quic-sim`, passes on `main`, fixed at its cause. ## Plan -[moq-dev/moq#6719](https://github.com/moq-dev/moq/pull/6719) found it failing -the same way on `main` and on its branch: a probe resends a stream after that -stream was reset. A reset stream's data is never retransmitted, so find which -path still queues it (probe padding, a loss-recovery retransmit, or a stale -send queue entry) and stop it there. The other probe tests in the release -suite pass, so the fix must keep them passing. +The expiry helper now records only actual resets. It previously marked a +completed stream as reset at its group's end time, falsely rejecting probe +packets sent before completion and still in flight. The reset assertion and +acceptance budgets remain unchanged. + +With that bookkeeping fixed, the release suite reaches a separate failure: +over seeds 1 through 8, probing averages 413.6 late frames versus 385.1 for +the baseline, beyond the existing deadline budget. Investigate why probing +beside Cubic adds delay and fix it at its source, preserving the overhead, +fairness, and rise budgets and the other probe scenarios. Do not relax the +assertions or disable probing to make the scenario pass. + +Run the probe suite with release debug assertions and overflow checks, as +`just rs quic-sim` does. `just rs test --release -p moq-quic --run-ignored all +--no-fail-fast -E 'test(/^tests::probe::/)'` also includes the short gate cases. Public API: none. Wire: none. diff --git a/rs/moq-quic/src/tests/probe.rs b/rs/moq-quic/src/tests/probe.rs index b6c364a7e2e..a0e618c9e0f 100644 --- a/rs/moq-quic/src/tests/probe.rs +++ b/rs/moq-quic/src/tests/probe.rs @@ -472,7 +472,7 @@ fn expire(pair: &mut Pair, ch: ConnectionHandle, groups: &mut [Group], now: Inst let mut stream = pair.client_send(ch, group.id); let offset = stream.offset(); let Ok(false) = stream.acked(offset) else { - group.reset = Some(ended); + // Completion is not a reset: older probe packets may still be in flight. continue; }; stream.reset(VarInt(0)).unwrap();