From a663fb8e28978a45030470d6de11b177d0500940 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 2 Nov 2026 15:13:08 -0800 Subject: [PATCH] quest: plan follow-ups from the 2026-11-02 /quest-complete round New quests for the follow-ups the maintainer picked, each linked to its source PR: moq-c's gated-callback use-after-free (m0 flakes), binary release publishers on their tag and the uid_map flake-lane skip (m0 ci), moq-crypto merging moq-sign and moq-e2ee, resumes more than one group behind, moq-quic's burst-sized idle state, and a typed stats epoch (m1), plus browser receive credit, JS fastest-reader pacing, the fMP4 1-byte-chunk bisect, relay bench pool gauges, ffmpeg multichannel FLAC in MKV, the relay idle timeout, and a feedback media test (m2). Edits: interop coverage adds draft 20; hls-reinit-same-codec requires the m0 declared-grid export; route recovery records the warm/cold cache resume; fastest-reader moves JS parity to m2 and gains a relay-level serve_ws test. Co-Authored-By: Claude Opus 5.5 --- quest/m0/ci/README.md | 2 + quest/m0/ci/release-tag-publishers.md | 17 +++++++++ quest/m0/ci/userns-uid-map.md | 15 ++++++++ quest/m0/test-flakes-2/README.md | 1 + .../m0/test-flakes-2/moq-c-gated-callback.md | 29 ++++++++++++++ quest/m1/README.md | 6 ++- quest/m1/hls-reinit-same-codec.md | 6 ++- quest/m1/ietf-resume-behind.md | 26 +++++++++++++ quest/m1/interop-coverage/README.md | 7 +++- quest/m1/moq-crypto.md | 38 +++++++++++++++++++ quest/m1/quic-readable-dedupe.md | 19 ++++++++++ quest/m1/stats-epoch-type.md | 15 ++++++++ quest/m1/unbounded-groups/fastest-reader.md | 16 ++++++-- quest/m2/README.md | 5 +++ quest/m2/bench-uring-pool.md | 13 +++++++ quest/m2/browser-receive-credit.md | 20 ++++++++++ quest/m2/fmp4-byte-chunk-perf.md | 20 ++++++++++ quest/m2/js-fastest-reader.md | 19 ++++++++++ quest/m2/mkv-flac-channel-mask.md | 15 ++++++++ quest/m2/route-health/README.md | 1 + quest/m2/route-health/idle-timeout.md | 12 ++++++ quest/m2/route-health/recovery.md | 2 + quest/m2/stats/README.md | 1 + quest/m2/stats/feedback-media-test.md | 13 +++++++ 24 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 quest/m0/ci/release-tag-publishers.md create mode 100644 quest/m0/ci/userns-uid-map.md create mode 100644 quest/m0/test-flakes-2/moq-c-gated-callback.md create mode 100644 quest/m1/ietf-resume-behind.md create mode 100644 quest/m1/moq-crypto.md create mode 100644 quest/m1/quic-readable-dedupe.md create mode 100644 quest/m1/stats-epoch-type.md create mode 100644 quest/m2/bench-uring-pool.md create mode 100644 quest/m2/browser-receive-credit.md create mode 100644 quest/m2/fmp4-byte-chunk-perf.md create mode 100644 quest/m2/js-fastest-reader.md create mode 100644 quest/m2/mkv-flac-channel-mask.md create mode 100644 quest/m2/route-health/idle-timeout.md create mode 100644 quest/m2/stats/feedback-media-test.md diff --git a/quest/m0/ci/README.md b/quest/m0/ci/README.md index a96b24693d7..e5510b4690f 100644 --- a/quest/m0/ci/README.md +++ b/quest/m0/ci/README.md @@ -161,6 +161,8 @@ The historical samples locate candidates; they are not controlled benchmarks. - [Verify the Nix cache](/quest/m0/ci/nix-cache-verify.md) - confirm releases use `nix.moq.dev`, check its bucket sizes, and retire Cachix - [Unify dependency features](/quest/m0/ci/hakari.md) - align the FFI builds' features and record the hosted platform jobs' hits once the workspace-hack lands - [Release credentials off main](/quest/m0/ci/publish-release-only.md) - `publish` admits only `release` and release tags once its `workflow_run` publishers run on their tag +- [Binary release publishers run on their tag](/quest/m0/ci/release-tag-publishers.md) - brew, Go and Swift releases stop running through `workflow_run` on main - [The queue host keeps the queue's disk](/quest/m0/ci/queue-host-disk.md) - agent sessions can't starve the queue runner of disk, and a crashed runner restarts - [Biome checks every file it can parse](/quest/m0/ci/biome-whole-repo.md) - Biome runs over the whole repository and main is clean under it - [One nextest pin](/quest/m0/ci/nextest-pin.md) - cargo-nextest's version lives in one place +- [The flake lane skips hosts that can't map user namespaces](/quest/m0/ci/userns-uid-map.md) - `check flake` skips instead of failing where `/proc/self/uid_map` is unwritable diff --git a/quest/m0/ci/release-tag-publishers.md b/quest/m0/ci/release-tag-publishers.md new file mode 100644 index 00000000000..625626776b3 --- /dev/null +++ b/quest/m0/ci/release-tag-publishers.md @@ -0,0 +1,17 @@ +# [S] Binary release publishers run on their tag + +## Goal + +`release-brew`, `release-go` and `release-swift-lib` run as their release tag, +not through `workflow_run` on `main`, like the publishers +[Release credentials off main](/quest/m0/ci/publish-release-only.md) moves. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7431](https://github.com/moq-dev/moq/pull/7431). They use +the `bot` Environment today, which admits `main`; moving them needs `bot` to +admit the binary release tags, a maintainer action to list in the PR. + +## Required + +- [Release credentials off main](/quest/m0/ci/publish-release-only.md) - the same move for the `publish` jobs lands first diff --git a/quest/m0/ci/userns-uid-map.md b/quest/m0/ci/userns-uid-map.md new file mode 100644 index 00000000000..99c75aed175 --- /dev/null +++ b/quest/m0/ci/userns-uid-map.md @@ -0,0 +1,15 @@ +# [XS] The flake lane skips hosts that can't map user namespaces + +## Goal + +`check flake` skips with a clear note, instead of failing, on a host whose +sandbox can't write `/proc/self/uid_map`, as it already does when user +namespaces are unavailable. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7461](https://github.com/moq-dev/moq/pull/7461), whose +local gate failed `check flake` building nixpkgs' `github-runner` with +`unshare: write failed /proc/self/uid_map`. `MOQ_USERNS_UNAVAILABLE` in +`sh/dispatch.sh` does not catch this case; probe the write itself. CI hosts +that can map namespaces must still run the lane. diff --git a/quest/m0/test-flakes-2/README.md b/quest/m0/test-flakes-2/README.md index e337b4d92d8..e379e19a9f8 100644 --- a/quest/m0/test-flakes-2/README.md +++ b/quest/m0/test-flakes-2/README.md @@ -73,3 +73,4 @@ Public API: none. Wire: none. - [moq-tokio's close-drain test survives load](/quest/m0/test-flakes-2/tokio-close-drain.md) - `quic_client_close_drains_finished_track` stops failing land batches with "Unroutable" - [@moq/net's close-drain tests survive load](/quest/m0/test-flakes-2/js-close-drain.md) - the lite close-drain cases pass on every full suite run - [@moq/net's boundary-sync test survives load](/quest/m0/test-flakes-2/js-boundary-sync.md) - "a version without a boundary syncs" passes under load +- [moq-c's gated callback test frees nothing early](/quest/m0/test-flakes-2/moq-c-gated-callback.md) - the moq-c tests stop aborting with a use-after-free under load diff --git a/quest/m0/test-flakes-2/moq-c-gated-callback.md b/quest/m0/test-flakes-2/moq-c-gated-callback.md new file mode 100644 index 00000000000..86c376b1b6f --- /dev/null +++ b/quest/m0/test-flakes-2/moq-c-gated-callback.md @@ -0,0 +1,29 @@ +# [XS] moq-c's gated callback test frees nothing early + +## Goal + +`rs/moq-c/src/test.rs`'s `gated_callback` never touches `user_data` after the +test may have dropped it, so the moq-c tests stop aborting under load with +`corrupted size vs. prev_size` (SIGABRT). The fix is at the cause, never a +retry or a sleep. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from the SIGABRT in +[moq-dev/moq#7461](https://github.com/moq-dev/moq/pull/7461)'s Check run +(`moq-c test::follow_delivers_the_instance_again_after_an_end`). + +- Cause: `gated_callback` sends `code` and only then locks `gate.hold`. + Cancelling the follow is asynchronous, so after the terminal `0` arrives the + test drops `gate` while the callback thread may still reach the freed + `Box`. The API guarantees `user_data` only until the terminal callback + (`rs/moq-c/src/api.rs`, around line 2384). Introduced by + [moq-dev/moq#5886](https://github.com/moq-dev/moq/pull/5886). +- Fix: take `gate.hold` before `gate.tx.send(code)`, so the test cannot free + the gate while the callback still holds it. It is the only test callback that + touches `user_data` after sending. +- Reproduce first: a temporary sleep between the send and the lock makes + valgrind report the invalid write; keep a regression that fails without the + fix if one is practical. + +Follow the rules in [More tests under load](/quest/m0/test-flakes-2/README.md). diff --git a/quest/m1/README.md b/quest/m1/README.md index b69c47953f6..fa88b1bc5f6 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -154,6 +154,7 @@ per-frame CPU growth since #5926. - [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 pin bump](/quest/m1/moxygen-pin-bump.md) - waiting on moxygen#264, then the TRACK_STATUS patch is dropped - [A smaller, safer API than release](/quest/m1/api-audit/README.md) - every surface added since release has a consumer or is gone, overlapping operations are collapsed, names mirror across Rust, JS, and the bindings, and every break says how to migrate +- [moq-crypto replaces moq-sign and moq-e2ee](/quest/m1/moq-crypto.md) - one Rust crate and one JS package hold signing and end-to-end encryption, sharing moq-net's timestamp check - [moq-transport 18, 20, and 22 only](/quest/m1/ietf-drop/README.md) - drafts 14-17, 19, and 21 are deleted from Rust, JS, tests, interop lanes, and docs, removing the legacy control-stream adapter and its request limit - [A moq-transport request token is one auth per distinct token](/quest/m1/ietf-token-auth.md) - requests naming the same token share one verification and grant, on any request type, ending with the last such request - [moq-quic's API improves on quinn's](/quest/m1/quic-api/README.md) - moq-quic and both QUIC drivers ship one mirrored API: `send::`/`recv::` namespaces, one error per direction, consuming terminal operations, plain configs, no quinn baggage @@ -170,7 +171,7 @@ per-frame CPU growth since #5926. - [crates.io trusts the workflow](/quest/m1/crates-trusted-publisher.md) - maintainer action: every published crate gets a trusted publisher for `release-rs.yml` - [Rust releases publish through OIDC](/quest/m1/release-rs-oidc.md) - release-plz publishes with trusted publishing and `CARGO_REGISTRY_TOKEN` is revoked - [Main is no slower than release](/quest/m1/release-perf/README.md) - every benchmark where main is slower than release is fixed or accepted with a reason, checked by a final paired run before the cut -- [Interop coverage](/quest/m1/interop-coverage/README.md) - the riskiest moq-transport features moq-net exposes get cross-implementation tests over drafts 18 and 22 +- [Interop coverage](/quest/m1/interop-coverage/README.md) - the riskiest moq-transport features moq-net exposes get cross-implementation tests over drafts 18, 20, and 22 - [Calibrate the paired nightly benchmarks](/quest/m1/bench-ci.md) - A/A and deliberately slowed Nightly runs set the paired comparison's threshold, so its regression alerts can be trusted - [HLS and DASH segment conformance](/quest/m1/hls-conformance.md) - nightly ffprobe checks that advertised timing, sync flags, and bytes match the media served - [Unbounded groups](/quest/m1/unbounded-groups/README.md) - groups and frames lose their size caps; a group keeps a 32 MB tail, and readers behind it replay through paced flights or refresh @@ -180,6 +181,9 @@ per-frame CPU growth since #5926. - [HLS export lists a same-codec reconfigure's first group under the new init](/quest/m1/hls-reinit-same-codec.md) - the hand-over pick, whichever of the catalog and the records arrives first, after the catalog `hls` removal is planned - [moq-quic deletes its remaining 0-RTT code](/quest/m1/quic-zero-rtt-leftovers.md) - only the code that drops a 0-RTT packet remains, with a test - [Gate the no-em-dash rule](/quest/m1/em-dash-gate.md) - `just check` fails on an em dash in any tracked text file +- [A resume more than one group behind never hangs](/quest/m1/ietf-resume-behind.md) - a resumed read gets every group or a drop on every wire version +- [moq-quic's idle connections keep no burst-sized state](/quest/m1/quic-readable-dedupe.md) - readable events are deduplicated and a quiet connection keeps no per-peak-stream bytes +- [Stats name the route instance's epoch with a type](/quest/m1/stats-epoch-type.md) - `GroupFrame::epoch` gets a dedicated type and tests stop mislabelling the instance ## Related diff --git a/quest/m1/hls-reinit-same-codec.md b/quest/m1/hls-reinit-same-codec.md index 84e3873137b..782af348e4d 100644 --- a/quest/m1/hls-reinit-same-codec.md +++ b/quest/m1/hls-reinit-same-codec.md @@ -12,10 +12,14 @@ Found by the conformance test in [moq-dev/moq#7387](https://github.com/moq-dev/m What remains is the hand-over race. The catalog and the records are separate tracks, so the new config's first group can reach the old instance's records watcher before the catalog update. The retiring instance claims it, so it is muxed under the old init and the replacement starts one group late. -Waiting on a plan: the maintainer decided on 2026-11-02 to remove the catalog's `hls` field (`rs/hang/src/catalog/hls.rs` and the JS zod schema), planned separately. That changes how the export finds a rendition's records, so the hand-over pick is designed after that plan, not with a catalog `start` field, a record track per config, or a marker in each record (all three dropped the same day). Once the removal is planned, add it under `Required` here or fold the pick into it. +The maintainer decided on 2026-11-02 to remove the catalog's `hls` field (`rs/hang/src/catalog/hls.rs` and the JS zod schema); that removal is now [HLS export on a declared grid](/quest/m0/hls-media-grid.md). It changes how the export finds a rendition's records, so the hand-over pick is designed after it lands, not with a catalog `start` field, a record track per config, or a marker in each record (all three dropped the same day). Public API: none expected. Wire: decided by the removal plan. +## Required + +- [HLS export on a declared grid](/quest/m0/hls-media-grid.md) - the catalog's `hls` field and record tracks go away first + ## Related - [HLS and DASH segment conformance](/quest/m1/hls-conformance.md) - the test that found it, which waits on this diff --git a/quest/m1/ietf-resume-behind.md b/quest/m1/ietf-resume-behind.md new file mode 100644 index 00000000000..e97e1693143 --- /dev/null +++ b/quest/m1/ietf-resume-behind.md @@ -0,0 +1,26 @@ +# [S] A resume more than one group behind never hangs + +## Goal + +A resumed read that starts more than one group behind the latest one gets +every group or a drop for it on every wire version, never a hang. The fix is +the simplest one that works; moq-transport FETCH/SUBSCRIBE edge cases are not +worth draft-specific fallbacks. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7631](https://github.com/moq-dev/moq/pull/7631), which +found it and kept its two direct bounded-resume tests on draft 22 only. + +- It hangs on `main` too, even with an open-ended range, over a draft-18 + upstream. Two causes: our moq-transport publisher answers only one-group + FETCHes, so the fill for the missing run never arrives (a one-group range + works); and the live join can skip a group that is already cached + (`TrackState::answered` in `rs/moq-net/src/model/track.rs`). +- Also check whether the moq-lite publisher has the same wait when it serves a + bounded SUBSCRIBE from a moq-transport copy that was only fetched. +- Multi-group past reads between our own moq-transport nodes are refused until + hole markers land (decided on + [moq-dev/moq#7625](https://github.com/moq-dev/moq/pull/7625)); a refused run + must come back as dropped, not hang. +- Enable the draft-18 cases of #7631's bounded-resume tests as the regression. diff --git a/quest/m1/interop-coverage/README.md b/quest/m1/interop-coverage/README.md index 3006b2d6c74..e8acedaa488 100644 --- a/quest/m1/interop-coverage/README.md +++ b/quest/m1/interop-coverage/README.md @@ -5,7 +5,7 @@ The moq-transport features moq-net exposes with the most interop bug history have cross-implementation tests: Rust, JS, and the bindings exchange hang media and exercise subgroup data, namespace discovery, late joins, -FETCH, GOAWAY, TRACK_STATUS, and datagrams over drafts 18 and 22. Our +FETCH, GOAWAY, TRACK_STATUS, and datagrams over drafts 18, 20, and 22. Our upstream runner clients implement the runner's existing cases for those features. @@ -26,6 +26,11 @@ Boundaries: ## Plan +Decided 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run): draft 20 joins the covered drafts, since it came back in +[moq-dev/moq#7190](https://github.com/moq-dev/moq/pull/7190) and every published moq-dev client and relay already offers it (verified on +[moq-dev/moq#6972](https://github.com/moq-dev/moq/pull/6972)). Where this Plan says drafts 18 and 22, read 18, 20, and 22. + + Decided with the maintainer on 2026-10-21, from an inventory of the coverage on `main` at `3ab6cbf81d`. diff --git a/quest/m1/moq-crypto.md b/quest/m1/moq-crypto.md new file mode 100644 index 00000000000..a36fc566047 --- /dev/null +++ b/quest/m1/moq-crypto.md @@ -0,0 +1,38 @@ +# [M] moq-crypto replaces moq-sign and moq-e2ee + +## Goal + +One crate, `moq-crypto`, and one JS package, `@moq/crypto`, hold MoQ's +content signing and end-to-end encryption, replacing `moq-sign`/`@moq/sign` +and `moq-e2ee`/`@moq/e2ee` before `main` is next cut into `release`. Both +halves share one timestamp check with moq-net, so they cannot drift from it. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run). The follow-up started from +[moq-dev/moq#7614](https://github.com/moq-dev/moq/pull/7614), which made +`moq_net::group::on_track` public; the maintainer asked to combine the two +packages instead of only deduplicating the check. + +Decisions: + +- ✅ Name it `moq-crypto` (Rust) and `@moq/crypto` (JS). Not chosen: + `moq-secure`; keeping `moq-e2ee` and absorbing signing into it. +- ✅ Merge JS too, for parity. `@moq/sign` and `@moq/e2ee` are private and + unpublished, so the rename costs nothing outside the repository. Not chosen: + Rust only. +- ✅ Plain `sign` and `e2ee` modules, always built. Not chosen: Cargo features + per half. +- ✅ `moq-e2ee` 0.0.19 is already on crates.io: at the cut, publish a final + `moq-e2ee` whose README points at `moq-crypto`. No yank. Not chosen: + leaving it as is; yanking it. Version levels stay with the maintainer. +- Both halves call `moq_net::group::on_track` instead of their own copies + (for example `rs/moq-sign/src/group.rs:48`). + +Keep the crate independent of hang and codec-specific publisher policy, as the +[E2EE epic](/quest/m2/e2ee/README.md) requires. Update every crate, package, +doc, binding, and quest that names the old packages, including the m2 E2EE +quests. + +Public API: the crate and package names and their import paths change. +Wire: none. diff --git a/quest/m1/quic-readable-dedupe.md b/quest/m1/quic-readable-dedupe.md new file mode 100644 index 00000000000..3b584bcd667 --- /dev/null +++ b/quest/m1/quic-readable-dedupe.md @@ -0,0 +1,19 @@ +# [S] moq-quic's idle connections keep no burst-sized state + +## Goal + +A moq-quic connection's per-stream "readable" event queue stays bounded by the +number of streams with unread data, not by the size of a burst, and a quiet +connection keeps no per-stream bytes for streams it no longer has. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7528](https://github.com/moq-dev/moq/pull/7528), which +stopped recycling freed receive state. + +- Deduplicate the per-stream readable events the connection queues; today the + queue can grow to the size of a burst. +- Trace the roughly 70 to 90 bytes per peak concurrent stream that a quiet + connection still keeps after #7528. The stream maps holding their peak + capacity are the likely part; release or shrink them at rest. +- Measure retained bytes after a burst with #7528's `receive_bursts` bench. diff --git a/quest/m1/stats-epoch-type.md b/quest/m1/stats-epoch-type.md new file mode 100644 index 00000000000..7852096a039 --- /dev/null +++ b/quest/m1/stats-epoch-type.md @@ -0,0 +1,15 @@ +# [XS] Stats name the route instance's epoch with a type + +## Goal + +`moq_stats::GroupFrame::epoch` carries a dedicated type rather than a bare +integer, and the routing contract tests stop labelling the instance's value +`epoch` where they mean the instance, so the public stats API matches the +instance/epoch naming before the cut. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), leftovers from [moq-dev/moq#7489](https://github.com/moq-dev/moq/pull/7489) +(routes carry an instance and a per-instance epoch). + +Public API: the `GroupFrame` field's type changes. diff --git a/quest/m1/unbounded-groups/fastest-reader.md b/quest/m1/unbounded-groups/fastest-reader.md index e63fbe07493..4f3bfbeaec5 100644 --- a/quest/m1/unbounded-groups/fastest-reader.md +++ b/quest/m1/unbounded-groups/fastest-reader.md @@ -17,8 +17,8 @@ The paced hops include qmux sessions (WebSocket, `tcp://`, `tls://`, messages are bounded by the session's receive window and the lowest-priority holder is evicted when it is stuck, as on native QUIC. The per-session memory a peer can make us hold is one deliberate, documented number across native -QUIC and qmux. The browser keeps reading unpaced until WebTransport offers a -real receive-credit contract. +QUIC and qmux. The browser and JS keep reading unpaced; their pacing is +[JS fastest-reader pacing](/quest/m2/js-fastest-reader.md), in m2. ## Plan @@ -208,5 +208,13 @@ What remains: added. The cursor's report is one relaxed load per frame or batch, and a `fetch_max` only for the fastest reader. On the busy host that landed it, runs swung by up to 50% either way between repeats. -- JS parity, once the browser offers a receive-credit contract. The JS qmux - receiver could pace today but follows the browser. +- A relay-level test that the operator's `[quic]` windows reach WebSocket + sessions end to end through the relay's own `serve_ws` path. Today it is + covered only at `websocket::accept` level, because an accepted session's + window is not visible outside moq-tokio. + +Decided 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 +/quest-complete run): JS parity moves to +[JS fastest-reader pacing](/quest/m2/js-fastest-reader.md) in m2, which waits +on [browser receive credit](/quest/m2/browser-receive-credit.md), so the m1 +cut does not wait on browsers. diff --git a/quest/m2/README.md b/quest/m2/README.md index 6bd93f2826b..9aead1c245b 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -241,3 +241,8 @@ lite-07; bitrate enforcement is not part of this release. - [The browser bench keeps its page consoles](/quest/m2/browser-bench-consoles.md) - publisher and consumer page logs in the run directory - [moq-quic's long header type is one variant](/quest/m2/quic-long-type.md) - collapse LongType after the 0-RTT removal - [One predicate for the lite close wait](/quest/m2/subscriber-fin-fold.md) - fold waits_for_subscriber_fin into has_track_stream +- [Browsers expose WebTransport receive credit](/quest/m2/browser-receive-credit.md) - condition: the page can tell how much credit an unread stream holds +- [JS fastest-reader pacing](/quest/m2/js-fastest-reader.md) - `@moq/net` paces upstream groups by the fastest reader, once browsers allow it +- [fMP4 import at 1-byte chunks is no slower than before](/quest/m2/fmp4-byte-chunk-perf.md) - bisect and fix or accept the ~19% slowdown at 1-byte chunks +- [The relay bench reports io_uring pool size and RSS](/quest/m2/bench-uring-pool.md) - pool growth regressions show up in normal bench runs +- [MKV imports ffmpeg's multichannel FLAC](/quest/m2/mkv-flac-channel-mask.md) - a channel-mask-only Vorbis comment is accepted, so default ffmpeg multichannel FLAC needs no remux diff --git a/quest/m2/bench-uring-pool.md b/quest/m2/bench-uring-pool.md new file mode 100644 index 00000000000..a71321e4a8a --- /dev/null +++ b/quest/m2/bench-uring-pool.md @@ -0,0 +1,13 @@ +# [XS] The relay bench reports io_uring pool size and RSS + +## Goal + +The `bench/` relay summary reports each run's io_uring buffer pool gauges +(receive and send, total and in use) and the relay's RSS, so a regression in +pool growth shows up in normal bench runs. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7233](https://github.com/moq-dev/moq/pull/7233), which +added the gauges to `/metrics` and measured them with a throwaway script. +Read them from the relay's `/metrics` at the end of each workload. diff --git a/quest/m2/browser-receive-credit.md b/quest/m2/browser-receive-credit.md new file mode 100644 index 00000000000..88d01ab5ff0 --- /dev/null +++ b/quest/m2/browser-receive-credit.md @@ -0,0 +1,20 @@ +# [XS] Browsers expose WebTransport receive credit + +## Goal + +Condition: WebTransport tells the page how much receive credit a stream it has +stopped reading still holds, in the spec and in shipping Chrome and Firefox. +Then [JS fastest-reader pacing](/quest/m2/js-fastest-reader.md) can pause +groups safely. Delete this quest once the condition holds. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), split out of [moq-dev/moq#7624](https://github.com/moq-dev/moq/pull/7624)'s +fastest-reader decisions (native only; browser parity waits on a real +receive-credit contract). `Reader.closed` watches resets, but nothing says how +much credit a paused stream holds, and guessed window constants would move +memory outside flow control. + +Check: the W3C WebTransport draft and the Chrome and Firefox implementations +for a receive-window or credit API on incoming streams (or per-stream +`highWaterMark`-style flow control that maps to QUIC credit). diff --git a/quest/m2/fmp4-byte-chunk-perf.md b/quest/m2/fmp4-byte-chunk-perf.md new file mode 100644 index 00000000000..bd3f555aea2 --- /dev/null +++ b/quest/m2/fmp4-byte-chunk-perf.md @@ -0,0 +1,20 @@ +# [S] fMP4 import at 1-byte chunks is no slower than before + +## Goal + +The interleaved fMP4 import fed in 1-byte chunks is back to its earlier cost, +or the slowdown is accepted with a recorded reason. Whole-input and 1 KiB +chunk imports are unaffected today. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7628](https://github.com/moq-dev/moq/pull/7628)'s +measurement: at 1-byte chunks `main` got about 19% slower (283 to 341 ms) +between [951cca678c](https://github.com/moq-dev/moq/commit/951cca678c3d02dd4671c41a5443567785832585) +and [96244dd854](https://github.com/moq-dev/moq/commit/96244dd854e4eeffedfdeaa9ef4a736266eb11b9), +separately from #7628. Suspects: +[moq-dev/moq#7491](https://github.com/moq-dev/moq/pull/7491), +[moq-dev/moq#7398](https://github.com/moq-dev/moq/pull/7398), +[moq-dev/moq#6985](https://github.com/moq-dev/moq/pull/6985) and +[moq-dev/moq#7218](https://github.com/moq-dev/moq/pull/7218). Bisect on a quiet +host through `quest gate`, in paired, order-rotated rounds. diff --git a/quest/m2/js-fastest-reader.md b/quest/m2/js-fastest-reader.md new file mode 100644 index 00000000000..edbe92ff7a1 --- /dev/null +++ b/quest/m2/js-fastest-reader.md @@ -0,0 +1,19 @@ +# [M] JS fastest-reader pacing + +## Goal + +`@moq/net` paces a group's upstream stream by its fastest reader like the +native path: it stops reading once a window past the fastest reader is +buffered, so stream flow control pushes back to the publisher, on WebTransport +and on the JS qmux receiver. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run): JS parity moves out of the m1 +[fastest-reader quest](/quest/m1/unbounded-groups/fastest-reader.md), so the m1 +cut does not wait on browsers. The JS qmux receiver could pace today but +follows the browser, so one design covers both. + +## Required + +- [Browsers expose WebTransport receive credit](/quest/m2/browser-receive-credit.md) - the page can tell how much credit an unread stream holds diff --git a/quest/m2/mkv-flac-channel-mask.md b/quest/m2/mkv-flac-channel-mask.md new file mode 100644 index 00000000000..1fd59dd7d17 --- /dev/null +++ b/quest/m2/mkv-flac-channel-mask.md @@ -0,0 +1,15 @@ +# [S] MKV imports ffmpeg's multichannel FLAC + +## Goal + +An MKV whose FLAC `CodecPrivate` holds STREAMINFO plus a Vorbis comment +carrying only `WAVEFORMATEXTENSIBLE_CHANNEL_MASK` imports, so ffmpeg's default +`-c:a flac` multichannel output (its default 6-channel 5.1, 2.1, 4.0, 5.0 and +7.1(wide)) needs no remux. Every other metadata block stays refused, and the +comment is bounded. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7629](https://github.com/moq-dev/moq/pull/7629), which +bounds FLAC's `CodecPrivate` at STREAMINFO and documents the remux. Bound the +vendor string and the single field by their syntax, not by a sampled constant. diff --git a/quest/m2/route-health/README.md b/quest/m2/route-health/README.md index ee0e871895e..4e6b89efd16 100644 --- a/quest/m2/route-health/README.md +++ b/quest/m2/route-health/README.md @@ -116,6 +116,7 @@ Add a route-health section to the routing concept page. - [Metadata subscriptions](/quest/m2/route-health/meta.md) - the current lite wip and the shared model observe frames without payload in Rust and JS - [Track route selection](/quest/m2/route-health/selection.md) - share broadcast identity/candidates while tracks choose sources independently - [Automatic recovery](/quest/m2/route-health/recovery.md) - epoch-only promotion in endpoints and relays, staying on the working route +- [Revisit the relay's default QUIC idle timeout](/quest/m2/route-health/idle-timeout.md) - keep or change the 10 s default, with the reason recorded ## Related diff --git a/quest/m2/route-health/idle-timeout.md b/quest/m2/route-health/idle-timeout.md new file mode 100644 index 00000000000..e7956641a8a --- /dev/null +++ b/quest/m2/route-health/idle-timeout.md @@ -0,0 +1,12 @@ +# [XS] Revisit the relay's default QUIC idle timeout + +## Goal + +moq-relay's default `quic.idle_timeout` (10 s) is either kept or changed, with +the reason recorded in the relay config doc, now that route recovery moves a +track before the idle timeout declares a dead route. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7096](https://github.com/moq-dev/moq/pull/7096)'s +route-health measurements. diff --git a/quest/m2/route-health/recovery.md b/quest/m2/route-health/recovery.md index 96d2d4563dd..2cc8bddc820 100644 --- a/quest/m2/route-health/recovery.md +++ b/quest/m2/route-health/recovery.md @@ -80,6 +80,8 @@ Public API: expose only health policy/evidence consumers need, mirrored across languages if public; settle consequential shape through quest-plan. Wire: uses Metadata subscriptions, with no separate health-specific route protocol. +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7096](https://github.com/moq-dev/moq/pull/7096): whether the in-progress group survives a resume depends on whether the upstream cache is warm or cold. Make it one defined behavior and test both cache states. + ## Required - [Metadata subscriptions](/quest/m2/route-health/meta.md) - progress-only standby and safe promotion diff --git a/quest/m2/stats/README.md b/quest/m2/stats/README.md index c838ce836ae..afa9fd4240c 100644 --- a/quest/m2/stats/README.md +++ b/quest/m2/stats/README.md @@ -125,6 +125,7 @@ connection view and the dashboard flow. - [Preflight](/quest/m2/stats/preflight.md) - a bounded test run over a broadcast reports which media layer is broken and why - [Client health in use](/quest/m2/stats/health-consumers.md) - a per-connection media counter, consumers of the grade, and re-measured limits +- [The media test checks browser feedback](/quest/m2/stats/feedback-media-test.md) - a browser publishes feedback and Rust reads it back with `moq export echo` ## Related diff --git a/quest/m2/stats/feedback-media-test.md b/quest/m2/stats/feedback-media-test.md new file mode 100644 index 00000000000..e21913509f8 --- /dev/null +++ b/quest/m2/stats/feedback-media-test.md @@ -0,0 +1,13 @@ +# [S] The media test checks browser feedback + +## Goal + +A cross-language media test has a browser `` publish feedback for a +soliciting catalog, and Rust reads it back with `moq export echo`, so a break +in either side's feedback format fails a test. + +## Plan + +Planned 2026-11-02 (maintainer, follow-up planning after the 2026-11-02 /quest-complete run), from [moq-dev/moq#7222](https://github.com/moq-dev/moq/pull/7222). Its +media test does not assert feedback yet because `just test media` drives its +own fixture rather than the demo.