diff --git a/quest/m0/README.md b/quest/m0/README.md index b10a7b72e91..91a780f3916 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -2,10 +2,7 @@ ## Goal -The work in flight now. Relay hardening: legal moq-transport input never fails -a session in current interop coverage, every resource a peer can make the -relay hold is bounded by what it sent or by a budget, and no peer input panics -the process. Alongside it: fixes for the `release` line, CI cost, and the +The work in flight now: fixes for the `release` line, CI cost, and the recurring audits. ## Plan @@ -14,8 +11,7 @@ The release API gates (#3829..#3878) and the release that followed them are done. Per-session request caps landed in #4820, idle fronts in #5054, and the -lost demand-poll wake in #5091. The rest of the 2026-09-29 DoS review remains -in m1 with relay session limits (status refreshed in the 2026-10-10 audit). +lost demand-poll wake in #5091. Routing: Wildcard landed in #4403, so a service claims the prefix it could serve instead of enumerating broadcasts. Serving the relay's ingested-only diff --git a/quest/m0/ci/nightly-ffi-reuse.md b/quest/m0/ci/nightly-ffi-reuse.md index 574826b84ff..e7a61de1c49 100644 --- a/quest/m0/ci/nightly-ffi-reuse.md +++ b/quest/m0/ci/nightly-ffi-reuse.md @@ -2,8 +2,9 @@ ## Goal -Condition: the first `moq-ffi-v*` tag whose commit carries -`.github/workflows/release-shared-ffi.yml` has run `Release FFI`, and a +Backport `.github/workflows/release-shared-ffi.yml` to `release` now as +release-line maintenance (decided 2026-10-22, maintainer), so the next +`moq-ffi-v*` tag carries it. Condition: that tag has run `Release FFI`, and a Nightly's `Shipping sizes` job has run since [#5549](https://github.com/moq-dev/moq/pull/5549). Check the tag with `git ls-tree .github/workflows/` and its runs with @@ -15,8 +16,10 @@ quest. ## Plan `moq-ffi-v0.4.16` (2026-10-16) was cut from a `release` without #5549, so it -ran the per-language workflows and verified nothing here. The Linux release -toolchain now links; verify `Shipping sizes` on the next scheduled Nightly. +ran the per-language workflows and verified nothing here; `moq-ffi-v0.4.17` +was likewise cut without it. `Shipping sizes` has run (Nightly +[39779729461](https://github.com/moq-dev/moq/actions/runs/39779729461), +2026-10-22): check its Go keys below, so only the tag condition remains. The first scheduled Nightly after the change (run 38897397428, 2026-10-16) already verified the dry-run side: the nested job names match the publisher diff --git a/quest/m0/ci/release-gates.md b/quest/m0/ci/release-gates.md index 1d6839625b7..a90ef11762c 100644 --- a/quest/m0/ci/release-gates.md +++ b/quest/m0/ci/release-gates.md @@ -2,8 +2,9 @@ ## Goal -Condition: the next cut carries `.github/workflows/release-gate.yml` to -`release`. Check with `git show origin/release:.github/workflows/release-gate.yml`. +`release` carries `.github/workflows/release-gate.yml`, backported now as +release-line maintenance rather than waiting for the next cut (decided +2026-10-22, maintainer). Check with `git show origin/release:.github/workflows/release-gate.yml`. Then the maintainer adds `Release gate` from the GitHub Actions integration (15368) to release ruleset 24392632, beside `Check`, so PRs into `release` diff --git a/quest/m1/README.md b/quest/m1/README.md index c3bb97b25f2..fa74b781c17 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -127,7 +127,7 @@ Binding durations wait on Duration support in the generated C. - [fMP4 edit lists](/quest/m1/fmp4-edit-lists.md) - the fMP4 importer applies edit lists to frame timestamps, so the interop AAC tone passes the phase check - [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-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 -- [Drop web-transport-trait](/quest/m1/drop-web-transport-trait.md) - qmux, iroh, wasm, and ffi implement moq-net's `transport::poll` traits, and the remaining `web_transport_trait` adapters are deleted +- [Drop web-transport-trait](/quest/m1/drop-web-transport-trait.md) - iroh, wasm, and ffi implement moq-net's `transport::poll` traits, and the remaining `web_transport_trait` adapters are deleted - [JavaScript FETCH](/quest/m1/js-fetch.md) - browser publishers answer IETF FETCH through the JS ranges request surface - [IETF fill timeout](/quest/m1/ietf-fill-timeout.md) - native FILL_TIMEOUT bounds upstream waiting on drafts 18-22, alongside local content-age enforcement - [Rust identity enforcement](/quest/m1/catalog-identity-rs.md) - `moq-mux` refuses a redefined rendition, and every Rust publisher mints a new rendition name for a decoder identity change and updates everything else in place diff --git a/quest/m1/api-audit/container-format.md b/quest/m1/api-audit/container-format.md index 67c9cc32361..3c1385ee549 100644 --- a/quest/m1/api-audit/container-format.md +++ b/quest/m1/api-audit/container-format.md @@ -36,3 +36,7 @@ passthrough, and `ts::Passthrough` is unreachable from `ContainerFormat`. `Result` like `Track`'s. Public API: breaking in moq-mux, moq-ffi, moq-c, and wrappers. Wire: none. + +## Required + +- [Generated C bindings](/quest/m1/c/README.md) - this changes the C API, so its C half lands on the generated package and C users break once (decided 2026-10-22, maintainer) diff --git a/quest/m1/api-audit/media-trim.md b/quest/m1/api-audit/media-trim.md index 4e85c7fc329..c2ee01231cb 100644 --- a/quest/m1/api-audit/media-trim.md +++ b/quest/m1/api-audit/media-trim.md @@ -17,11 +17,10 @@ Decided 2026-10-22 (API audit). Make `pub(crate)` or delete: - moq-video capture `Control`/`Driver` (only `publish_capture` is used), `DmaBuf::new`, `DmaBufLayout` (until [OBS Linux GPU](/quest/m3/obs-linux-gpu.md)). -- moq-hls `pub mod master`, range playlists, and `history`: the CLI never - sets history, so `?start=` always answers 416 while `doc/bin/hls.md` - documents it. Remove the docs with it; see - [history floor](/quest/m1/archive/history-floor.md). -- moq-archive `segment::*`, `Key`, and most of `Store`. +- moq-hls `pub mod master`. History mode and range playlists stay public: + the archive line ([history floor](/quest/m1/archive/history-floor.md), + [catalog HLS](/quest/m1/archive/catalog-hls.md)) is their consumer + (decided 2026-10-22, maintainer). - moq-json `window::Checkpoint`, `Encoder::resume`, `Producer::{resume, cut}` and `moq_mux::timeline::Producer::resume`, reached only by tests, unless the [archive consumer](/quest/m1/archive/consumer.md) needs them. diff --git a/quest/m1/api-audit/net-exports.md b/quest/m1/api-audit/net-exports.md index 34773440fa7..02ca6aca806 100644 --- a/quest/m1/api-audit/net-exports.md +++ b/quest/m1/api-audit/net-exports.md @@ -41,6 +41,8 @@ Decided 2026-10-23 (API audit follow-up, maintainer): Kept, so the next audit skips them: `transport::memory` stays public and ungated, since an in-process transport is real API that other crates' tests -use. +use. `Route::{auth, with_auth}` stay public, since `doc/concept/standard.md` +documents them and [auth issuer](/quest/m1/auth/issuer.md) extends them +(decided 2026-10-22, maintainer). Public API: removals and renames in moq-net and `@moq/net`. Wire: none. diff --git a/quest/m1/api-audit/transport-config.md b/quest/m1/api-audit/transport-config.md index 863d1fe2cb2..2a56210674a 100644 --- a/quest/m1/api-audit/transport-config.md +++ b/quest/m1/api-audit/transport-config.md @@ -33,9 +33,9 @@ Decided 2026-10-22 (API audit): path (`[listen.lb]`). - moq-quic's unused features, `rustls` alias, and crate docs moved to [quic-api trim](/quest/m1/quic-api/trim.md) (2026-10-23). -- Public only for tests: `lease::Producer::{due, failed}` and `Due` go - `pub(crate)` (drop the `tokio` feature), `connect::Config.quic` and +- Public only for tests: moq-auth `lease::Producer::{due, failed}` and `Due` + go `pub(crate)` (drop moq-auth's `tokio` feature), `connect::Config.quic` and `listen::Config.quic` (read only by `deprecated()`) go `pub(crate)`, and moq-uring `manual` and kio `sim` are renamed `_manual`/`_sim` or gated. -Public API: moq-tokio breaking. Wire: none. +Public API: moq-tokio, moq-auth breaking. Wire: none. diff --git a/quest/m1/audio-jitter-target/capped-budgets.md b/quest/m1/audio-jitter-target/capped-budgets.md index b0f1d840bfb..46e0dda4743 100644 --- a/quest/m1/audio-jitter-target/capped-budgets.md +++ b/quest/m1/audio-jitter-target/capped-budgets.md @@ -21,8 +21,7 @@ from before the one-ring removal also carry `-isolated` rows; grade only the Decided 2026-10-22 (maintainer): the Opus near-zero budgets quest folded in here, so one pass over the same seven nightlies covers both. The cap ([#6164](https://github.com/moq-dev/moq/pull/6164)) merged 2026-10-22T00:10Z, -after the build flush, so post-cap nightlies are also post-flush; none had run -yet. +after the build flush, so post-cap nightlies are also post-flush. The first post-cap scheduled run, [2026-10-22 nightly](https://github.com/moq-dev/moq/actions/runs/39779729461), failed only the browser Opus `wide` target ceilings: `isolated` p95 was diff --git a/quest/m1/drop-web-transport-trait.md b/quest/m1/drop-web-transport-trait.md index 8ecc4142273..8fb5bc8a3ca 100644 --- a/quest/m1/drop-web-transport-trait.md +++ b/quest/m1/drop-web-transport-trait.md @@ -4,7 +4,7 @@ moq-tokio, moq-uring, moq-relay, moq-ffi and moq-wasm no longer depend on `web-transport-trait`. Native QUIC already implements moq-net's traits -([native WebTransport](/quest/m1/quic-api/native-web.md)); the qmux, iroh, wasm and ffi backends implement moq-net's +([native WebTransport](/quest/m1/quic-api/native-web.md)); the iroh, wasm and ffi backends implement moq-net's `transport::poll` traits, either directly or through a thin adapter per backend. The generic adapters over `web_transport_trait` are deleted: moq-tokio's `transport::Session` with its async-to-poll emulation, and @@ -21,7 +21,7 @@ sealed the adapters' bound to the crate's own backends, so a foreign `web_transport_trait::Session` no longer fits them. This quest removes the layer instead of reopening it. -- qmux and iroh get their own impls. Flat backends use `Ungrouped`. +- iroh gets its own impl. Flat backends use `Ungrouped`. - The browser backend in moq-wasm and the ffi backend implement the traits directly. Their adapters already clamp priorities themselves. - Report the public API impact per crate: deleted generic `Session` diff --git a/quest/m1/interop-coverage/README.md b/quest/m1/interop-coverage/README.md index 5a0880a5e0d..ffaeeaae355 100644 --- a/quest/m1/interop-coverage/README.md +++ b/quest/m1/interop-coverage/README.md @@ -12,10 +12,13 @@ existing cases for those features. Boundaries: -- Only what moq-net can do. PUBLISH (push), subgroups other than 0, - outbound forward=0, rendezvous, and per-request authorization tokens are - not exposed, so they are out of scope, and runner cells that need them - stay red by design. +- Only what moq-net can do. Subgroups other than 0, rendezvous, and + per-request authorization tokens are not exposed, so they are out of + scope, and runner cells that need them stay red by design. PUBLISH (push) + and outbound forward=0 join coverage now that SUBSCRIBE_TRACKS outbound + ([#6280](https://github.com/moq-dev/moq/pull/6280)) and IETF subscribe + pause ([#6299](https://github.com/moq-dev/moq/pull/6299)) have landed + (decided 2026-10-22, maintainer). - Media stays hang. No MSF catalog in our runner clients. - New cases live in-tree (`test/interop`), not in the runner's spec. - Not covered: mid-subscription updates (`Subscription::update` in Rust and diff --git a/quest/m1/lite07-finalize.md b/quest/m1/lite07-finalize.md index d51939691ba..afe1e1bc7b9 100644 --- a/quest/m1/lite07-finalize.md +++ b/quest/m1/lite07-finalize.md @@ -37,8 +37,10 @@ stability. Both are additive and, per the freeze policy, target whichever lite wip is current when they land. Decided 2026-10-08: Restart and AUTH join Required, since both change the -lite-07 wire. Decided 2026-10-17: compressed tracks join too, so lite-07 -ships with them. SUBSCRIBE_OK carrying live media time was dropped: no reader +lite-07 wire. Compressed tracks joined on 2026-10-17 and landed +([#5928](https://github.com/moq-dev/moq/pull/5928), +[#6228](https://github.com/moq-dev/moq/pull/6228), +[#6230](https://github.com/moq-dev/moq/pull/6230)). SUBSCRIBE_OK carrying live media time was dropped: no reader was named, and `set_live` covers it. The cut: @@ -65,16 +67,13 @@ Required, because it adds `Max Age` to lite-07's SUBSCRIBE_DROP. Only line, because it is that line's lite-07 wire work; the rest is API work that does not block the rename. -Decided 2026-10-17: the archive line's discovery request joins Required, so -lite-07 ships what the archive and the IETF SUBSCRIBE_TRACKS mapping need. -On 2026-10-19 TRACKS, a per-broadcast track listing, replaced SUBSCRIBE_ALL -and kept that slot. +The archive line's TRACKS request (a per-broadcast track listing that +replaced SUBSCRIBE_ALL) landed in [#6158](https://github.com/moq-dev/moq/pull/6158) +and [#6167](https://github.com/moq-dev/moq/pull/6167), and the padding stream +type for the m2 browser [padding probes](/quest/m2/probe/padding.md) landed in +[#6233](https://github.com/moq-dev/moq/pull/6233). The `Time(pts)` range end rides in Subscribe ranges, already Required. -Decided 2026-10-20: the padding stream type joins Required, so the m2 -browser [padding probes](/quest/m2/probe/padding.md) can use lite-07 without -a new version. - Rust's lite-07 varints already carry the full 64 bits the draft specifies, so no codec work waits on the cut. diff --git a/quest/m1/read-backfill.md b/quest/m1/read-backfill.md index b5947305b18..b6293eac558 100644 --- a/quest/m1/read-backfill.md +++ b/quest/m1/read-backfill.md @@ -36,9 +36,9 @@ delivers a group that arrives below the cursor as a late backfill; Rust `largest()`, sparse moq-transport groups break the arithmetic, and a later coalesced largest isn't this request's resolution. SUBSCRIBE_OK's `Group` isn't it either, since the lite draft lets a group between the requested - start and it arrive later. If the wire doesn't carry the resolved start, - add it to lite-07 through [Lite-07 ranges](/quest/m1/subscribe-ranges/lite.md); - settle the moq-transport equivalent or keep today's first-arrival cursor + start and it arrive later. Lite-07 SUBSCRIBE_OK carries the resolved start + for a relative range start (decided 2026-10-22 in + [Lite-07 ranges](/quest/m1/subscribe-ranges/lite.md)); settle the moq-transport equivalent or keep today's first-arrival cursor there. - The cursor is range-aware, not just floored: with disjoint ranges it skips straight across an unrequested gap (group 1, then 10 onward) instead of @@ -54,8 +54,8 @@ delivers a group that arrives below the cursor as a late backfill; Rust API instead. Public API: no type change; the ordered read changes in Rust and JS (the -floor comes from reported bounds, and JS drops backfill). Wire: possibly a -resolved start on lite-07 SUBSCRIBE_OK, through Lite-07 ranges. +floor comes from reported bounds, and JS drops backfill). Wire: the resolved +start on lite-07 SUBSCRIBE_OK, through Lite-07 ranges. ## Required diff --git a/quest/m1/release-audit/README.md b/quest/m1/release-audit/README.md index 63434a8bf8d..b4de8e99acd 100644 --- a/quest/m1/release-audit/README.md +++ b/quest/m1/release-audit/README.md @@ -75,7 +75,7 @@ server state after final drop, idle Dart stream cancellation, empty expiry indexes, JS IETF timestamp changes, JS datagram range violations, and OBS reordered-frame timestamps and missing EOF output. Each owning quest records the reproduction's limits. JS reader and publisher datagram paths now enforce -subscription bounds; the OBS quest retains ownership of its findings. +subscription bounds; the OBS defects moved to an m1 child (2026-10-22, maintainer). Go reservation disposal remains an investigation. No platform-wide binding parity, full OBS runtime, fuzzing campaign, or network-impairment performance result is implied by these checks. @@ -85,10 +85,18 @@ The 2026-10-22 binding and OBS audit at main rate recovery child. Its two OBS findings went to m2 next to the other OBS work, because obs-moq ships on its own schedule. +## Required + +The entries comprise reproduced or source-confirmed findings +and contract/lifetime investigations. Each +child records whether its behavior is new on main or inherited from release. + +- [OBS decode timing](/quest/m1/release-audit/obs-decode-timing.md) - the shipping OBS source stamps each reordered picture with its own timestamp and drains at clean EOF + ## Related - [JS subscription ranges](/quest/m1/subscribe-ranges/js.md) - range lists and frame offsets build on the bounds fixed here -- [OBS video source](/quest/m2/obs-moq-video/source.md) - reproduced inherited timing and EOF defects remain with the decoder replacement owner +- [OBS video source](/quest/m2/obs-moq-video/source.md) - the decoder replacement keeps the timing and EOF behavior fixed here - [Compressed tracks](/quest/m1/compress/README.md) - mid-group widening and application migration have existing owners - [Archive consumer](/quest/m1/archive/consumer.md) - current producer and CLI reader still need the new-format roundtrip - [Measured release performance](/quest/m1/release-perf/README.md) - existing paired benchmark regressions and their confirmation runs diff --git a/quest/m1/release-audit/obs-decode-timing.md b/quest/m1/release-audit/obs-decode-timing.md new file mode 100644 index 00000000000..10dd4e7d967 --- /dev/null +++ b/quest/m1/release-audit/obs-decode-timing.md @@ -0,0 +1,26 @@ +# [S] The OBS source keeps decoded timestamps and drains at EOF + +## Goal + +On the current FFmpeg path in `cpp/obs/src/moq-source.cpp`, each decoded +picture carries its own presentation timestamp, not the input packet's, and +a clean media EOF drains the decoder's delayed pictures. A reordered H.264 +stream with B-frames plays every picture in order with correct timestamps. + +## Plan + +Split into m1 from the [OBS video source](/quest/m2/obs-moq-video/source.md) +replacement on 2026-10-22 (maintainer), so the cut does not ship the two +defects the release audit reproduced: `moq_source_decode_frame` assigns the +current packet's timestamp to an older reordered picture, and the clean EOF +callback returns without draining (about `moq-source.cpp:217` and `:634` at +1d43c096e0). The audit's libavcodec reproduction (12 B-frames, 10 pictures +all mis-stamped, two lost at EOF) is the regression test to automate. + +Use the decoder's output timestamp (`best_effort_timestamp` or the frame's +`pts`). On clean EOF, send a flush packet and call `avcodec_receive_frame` +until it returns `AVERROR_EOF`, emitting every picture; the regression +asserts all 12 pictures and their timestamps. Keep the fix inside the +current path; the GPU replacement keeps the same behavior. + +Public API: none. Wire: none. diff --git a/quest/m1/subscribe-ranges/lite.md b/quest/m1/subscribe-ranges/lite.md index e87612bd4d1..a63e592d340 100644 --- a/quest/m1/subscribe-ranges/lite.md +++ b/quest/m1/subscribe-ranges/lite.md @@ -54,8 +54,7 @@ list. Update `drafts/draft-lcurley-moq-lite.md` in the same PR: the SUBSCRIBE and SUBSCRIBE_UPDATE fields, the Group Order section (the order is now a field), -the removal of FETCH, and the lite-07 changelog. Fix the stale "offset by 1" -sentence on Group Start while there. This quest also owns the Order row of +the removal of FETCH, and the lite-07 changelog. This quest also owns the Order row of the priority table in `doc/concept/moq-lite.md`: it now describes the field (decided 2026-10-08, since subscribe ranges owns group order). Published versions (lite-03..06) keep FETCH, answered through the model's ranges. Run `just drafts check` and diff --git a/quest/m1/ts-reliability/README.md b/quest/m1/ts-reliability/README.md index 9a95475434b..50f07919b6b 100644 --- a/quest/m1/ts-reliability/README.md +++ b/quest/m1/ts-reliability/README.md @@ -93,8 +93,8 @@ absorbs the release audit's export-reorder quest, and the shared parser lands before the open stream-routes and PAT drafts, which rebase onto it. PMT fragmentation (#5220), initial mux-rate measurement (#5287), and -decoder-time admission (#5306) have landed; PSI runs and catalog linger -remain with their current quests. +decoder-time admission (#5306) have landed, as have PSI runs (#5606) and +catalog linger (#5305). ## Required diff --git a/quest/m2/README.md b/quest/m2/README.md index a1c9c702242..38ed382503d 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -62,9 +62,9 @@ lite-07; bitrate enforcement is not part of this release. - [Failed groups reach the publisher](/quest/m2/group-failed.md) - Rust and both JS publishers surface a group whose delivery a subscriber's stream gave up on, with one shape - [Headless moq-transport groups](/quest/m2/ietf-headless-group.md) - a group whose head no fill or joining FETCH supplies keeps its tail instead of being dropped, and a relay forwards it to moq-transport subscribers - [A finished PROBE stops reporting](/quest/m2/probe-end-stale.md) - the Rust receive estimate reads unknown once its PROBE stream ends -- [Wire stats](/quest/m2/wire-stats/README.md) - `Presence` counts each root's transport bytes on the wire, `wire_tx` and `wire_rx`, from QUIC, WebSocket, and embedder gateway sources - [Media stats](/quest/m2/stats/README.md) - publishers announce a stats track in the catalog, viewers answer a soliciting catalog through a per-catalog `.echo` broadcast, and one model turns both into a health verdict and a preflight report - [Perf](/quest/m2/perf/README.md) - eliminate measured hot-path costs across moq-uring, kio, and the moq-net model +- [Wire stats](/quest/m2/wire-stats/README.md) - `Presence` counts each root's transport bytes on the wire, `wire_tx` and `wire_rx`, from QUIC, WebSocket, and embedder gateway sources - [Performance survey follow-ups](/quest/m2/performance-survey/README.md) - benchmark the source audit's transport, browser, media and serving risks before optimizing - [Nightly fuzzing keeps its corpus](/quest/m2/fuzz-corpus.md) - each fuzz target resumes from the previous Nightly's minimized corpus instead of starting from seeds - [Relay session bench](/quest/m2/bench-relay.md) - the same scenario through moq-relay's own connection handling @@ -120,6 +120,7 @@ lite-07; bitrate enforcement is not part of this release. - [moqsrc A/V sync](/quest/m2/moqsrc-av-sync.md) - a nightly test measures A/V skew from `moqsrc` through clock-synced sinks - [MP4 export](/quest/m2/mp4-export.md) - `moq export mp4 --output` records crash-safe fragments, then finishes a regular MP4 with moov at the end - [Importers fill the VR fields](/quest/m2/mux-vr-fields.md) - MP4, Matroska, and Opus imports set the catalog's projection, stereo, and ambisonic layout +- [fMP4 ambisonic layout](/quest/m2/mux-vr-ambisonic-fmp4.md) - fMP4 Opus import fills `ambisonic` from the `dOps` mapping table - [Linux decoded frames](/quest/m2/obs-decode-linux.md) - present supported native decoded surfaces with visible CPU fallback - [Windows decoded frames](/quest/m2/obs-decode-windows.md) - present decoded D3D11 surfaces in OBS without CPU readback - [macOS GPU input](/quest/m2/obs-macos.md) - feed the encoder from the OBS compositor without CPU readback diff --git a/quest/m2/audio-encode-mediafoundation.md b/quest/m2/audio-encode-mediafoundation.md index 18b95360dcd..fdddf6ec253 100644 --- a/quest/m2/audio-encode-mediafoundation.md +++ b/quest/m2/audio-encode-mediafoundation.md @@ -20,8 +20,6 @@ behind the encode seam on Windows. ## Required +- [AudioToolbox encode](/quest/m2/audio-encode-audiotoolbox.md) - changes `Encoder::encode` to a zero-or-more packet return with a flush, which this backend implements - [Media Foundation decode](/quest/m2/audio-decode-mediafoundation.md) - the round-trip regression decodes through it -## Related - -- [AudioToolbox encode](/quest/m2/audio-encode-audiotoolbox.md) - the macOS and iOS backend, which may already add `flush` to `Encoder::encode` diff --git a/quest/m2/audit-deps.md b/quest/m2/audit-deps.md index 3fdfc69584b..da99771b20c 100644 --- a/quest/m2/audit-deps.md +++ b/quest/m2/audit-deps.md @@ -10,7 +10,7 @@ replacement. Recurs once per major release (decided 2026-10-19). The PR that finishes this run also adds the next one with the same file name at the end of the next -milestone's `Required` (m0 to m1, m1 to m2, m2 to m3). The successor keeps the Goal +milestone's `Required` (m0 to m1, m1 to m2). The successor keeps the Goal and this paragraph; the rest of its Plan is rewritten with what this run learned: baselines, results, and what to check first. A successor placed in m2 also lists the [release cut](/quest/m2/release/cut.md) under `Required`, so it diff --git a/quest/m2/audit-test-cost.md b/quest/m2/audit-test-cost.md index 64cbf804997..132b8baabac 100644 --- a/quest/m2/audit-test-cost.md +++ b/quest/m2/audit-test-cost.md @@ -65,13 +65,12 @@ Check first: stops counting an unanswered handshake as active immediately while the closing period still answers late packets. Do not repeat the old request to skip draining. [moq-dev/moq#6240](https://github.com/moq-dev/moq/pull/6240) - proposes retiring the silent-peer quest as complete. + retired the silent-peer quest as complete. - moq-uring: three endpoint tests wait out moq-quic's fixed 10 s handshake idle timeout (30 s together). Neither moq-uring's `Transport` nor `--quic-idle-timeout` sets it. #6201 does not change that timeout. The - [retirement proposal](https://github.com/moq-dev/moq/pull/6240) records - keeping it separate with no new knob; follow its maintainer decision - rather than independently selecting another API in a cost audit. + [retirement](https://github.com/moq-dev/moq/pull/6240) settled keeping it + separate with no new knob; do not select another API in a cost audit. - CPU-bound simulations in a debug build: five TS export timing cases (3 to 11 s each) and the jitter buffer's day of drift (6 to 13 s), whose `Steer` rebuilds the hull and median over its whole window on every step. diff --git a/quest/m2/bench-transport-sweep.md b/quest/m2/bench-transport-sweep.md index 7a531f93ea0..3ec4ab07907 100644 --- a/quest/m2/bench-transport-sweep.md +++ b/quest/m2/bench-transport-sweep.md @@ -14,7 +14,10 @@ in-memory transport only. Add the transports a production session uses, moq-quic and qmux at least, keeping each run deterministic: drive QUIC over an in-memory datagram link rather than real sockets if the noise demands it. Keep existing bench IDs stable so their history carries over; new -transports add series rather than renaming old ones. +transports add series rather than renaming old ones. This bench owns the +qmux series in the regression table; the +[stream transport](/quest/m2/perf/stream/bench.md) micro-bench stays a local +profiling tool (decided 2026-10-22, maintainer). Public API: none. Wire: none. @@ -23,3 +26,4 @@ Public API: none. Wire: none. - [Benchmark sources](/quest/m2/bench-sources.md) - feeds these series into the regression table - [Relay session bench](/quest/m2/bench-relay.md) - the same scenario through moq-relay - [QUIC on the sim](/quest/m2/quic-sim.md) - an in-memory QUIC link the sweep could reuse +- [Stream transport bench](/quest/m2/perf/stream/bench.md) - a Criterion qmux micro-bench over tcp, tls, ws and wss diff --git a/quest/m2/broadcast-fairness.md b/quest/m2/broadcast-fairness.md index aac8542d2dd..e8ca4d60634 100644 --- a/quest/m2/broadcast-fairness.md +++ b/quest/m2/broadcast-fairness.md @@ -86,7 +86,7 @@ good plan here". Open questions it left: - Subscriber aggregate forwarding: under a fair schedule the downstream max should stop being forwarded upstream at its source, not just be ignored by the next fair hop. -- Transports without send domains (WebSocket, iroh, today's qmux) and +- Transports without send domains (iroh, the browser backend) and moq-transport sessions: refuse, or degrade to strict. - A cheaper capability probe than opening and dropping a domain. diff --git a/quest/m2/intra-refresh/dual-decoder.md b/quest/m2/intra-refresh/dual-decoder.md index 5daa2928ce3..46e5db99a25 100644 --- a/quest/m2/intra-refresh/dual-decoder.md +++ b/quest/m2/intra-refresh/dual-decoder.md @@ -34,7 +34,8 @@ Decided in the 2026-10-14 planning interview: rounded-down or stale `warmup` only shrinks its budget and costs extra stripes, never a dirty frame the min-delay decoder wasn't also showing. This quest therefore does not wait on - [plan-boundary](/quest/m2/intra-refresh/plan-boundary.md). + [plan-boundary](/quest/m2/intra-refresh/plan-boundary.md); the warmup draft + amendment it needed landed in [#6494](https://github.com/moq-dev/moq/pull/6494). - Selection is a small frame selector over the two current candidates: show the max-delay frame when its timestamp is at or beyond the min-delay frame's, so it wins ties, else the min-delay frame. Separately, never display a diff --git a/quest/m2/intra-refresh/plan-boundary.md b/quest/m2/intra-refresh/plan-boundary.md index 1311e30ce4a..1d27b9840b6 100644 --- a/quest/m2/intra-refresh/plan-boundary.md +++ b/quest/m2/intra-refresh/plan-boundary.md @@ -9,10 +9,10 @@ earlier ones. Rewrite the import and consumer quests around that contract. ## Plan The 2026-10-11 TS audit found the proposed catalog duration is not yet a -sufficient guarantee. `h264-import` rounds a measured recovery duration down -to milliseconds and raises the global value after observing a longer sweep. +sufficient guarantee. `h264-import` raises the global value after observing +a longer sweep. `consumer-warmup` sizes the transcoder's decode history from that duration. A -rounded value can leave the history short of the recovery picture, and a later catalog update cannot +later catalog update cannot recall pictures a subscriber has already displayed using the old value. Decided 2026-10-22 (maintainer): precision is settled; the hang draft rounds a diff --git a/quest/m2/intra-refresh/play-dual-decoder.md b/quest/m2/intra-refresh/play-dual-decoder.md index 0b2bc6b6474..a83046bacd5 100644 --- a/quest/m2/intra-refresh/play-dual-decoder.md +++ b/quest/m2/intra-refresh/play-dual-decoder.md @@ -24,3 +24,4 @@ once it catches up. Playback of renditions without `warmup` is unchanged. - A CLI playback test over a synthetic `warmup` track: a cold join presents min-delay pictures and then max-delay ones, a skip recovers the same way, and pacing and A/V timing match single-decoder playback. + diff --git a/quest/m2/mux-vr-ambisonic-fmp4.md b/quest/m2/mux-vr-ambisonic-fmp4.md new file mode 100644 index 00000000000..24d54bbc01a --- /dev/null +++ b/quest/m2/mux-vr-ambisonic-fmp4.md @@ -0,0 +1,23 @@ +# [S] fMP4 import fills the ambisonic layout + +## Goal + +An fMP4 Opus track with channel mapping family 2 imports with the catalog's +`ambisonic` field, under the same RFC 8486 rules the Matroska and raw Opus +importers apply in [VR fields](/quest/m2/mux-vr-fields.md). + +## Plan + +Split from VR fields on 2026-10-22 (maintainer): only this part needs the +`dOps` channel mapping table, so the rest of VR fields does not wait on it. +Read the mapping from the `dOps` that +[CMAF surround Opus](/quest/m2/cmaf-opus-surround.md) carries through the +OpusHead, and reuse VR fields' family 2 validation. Test with an fMP4 +first-order ambisonic fixture and a refused non-diegetic stereo one. + +Public API: none. Wire: none. + +## Required + +- [CMAF surround Opus](/quest/m2/cmaf-opus-surround.md) - the `dOps` mapping table an ambisonic fMP4 track needs +- [VR fields](/quest/m2/mux-vr-fields.md) - the ambisonic validation and catalog plumbing this reuses diff --git a/quest/m2/mux-vr-fields.md b/quest/m2/mux-vr-fields.md index 5628aebbc9b..ebca62a2487 100644 --- a/quest/m2/mux-vr-fields.md +++ b/quest/m2/mux-vr-fields.md @@ -38,9 +38,11 @@ landed the fields. take mp4-atom 0.17.0 (kixelated/mp4-atom#247, not yet released), whose sample entries gain a `spatial` field. Importing Apple's `vexu`/`hfov` stays out of scope. `webm-iterable` already names the Matroska elements. -- An fMP4 Opus track needs the `dOps` mapping table that - [CMAF surround Opus](/quest/m2/cmaf-opus-surround.md) adds, so ambisonic - fMP4 follows it; Matroska and raw Opus do not wait. +- Ambisonic fMP4 needs the `dOps` mapping table, so it moved to + [fMP4 ambisonic layout](/quest/m2/mux-vr-ambisonic-fmp4.md) (2026-10-22, + maintainer); Matroska and raw Opus land here. Before taking 0.17.0, + settle with [CMAF surround Opus](/quest/m2/cmaf-opus-surround.md) whether + the release also carries kixelated/mp4-atom#246. - Exporters writing the fields back are out of scope. Tests: a fixture per source (MP4 360, a 180-degree equirectangular MP4 with @@ -51,10 +53,6 @@ with non-diegetic stereo, and a malformed one are refused. Public API: none beyond the catalog fields #5543 adds. Wire: none. -## Required - -- [CMAF surround Opus](/quest/m2/cmaf-opus-surround.md) - the `dOps` mapping table an ambisonic fMP4 track needs - ## Related - [WebXR playback](/quest/m3/watch-webxr.md) - `` renders these fields diff --git a/quest/m2/obs-moq-video/source.md b/quest/m2/obs-moq-video/source.md index 11dcf3a9e2c..9c5c74e692d 100644 --- a/quest/m2/obs-moq-video/source.md +++ b/quest/m2/obs-moq-video/source.md @@ -23,11 +23,11 @@ reordering returns an older picture. The clean media EOF callback also returns without draining delayed pictures. An algorithm-level reproduction using real libavcodec and 12 H.264 B-frames produced 10 pictures, all with incorrect assigned timestamps; explicit EOF draining recovered two omitted -pictures. This was not a running OBS integration test. Preserve decoded -output timestamps and drain clean EOF in the replacement, and cover both -with reordered video. Track these release concerns here rather than creating -a competing decoder rewrite; this evidence does not make the entire GPU -replacement a prerequisite for the release. +pictures. This was not a running OBS integration test. +[OBS decode timing](/quest/m1/release-audit/obs-decode-timing.md) fixes both +on the current FFmpeg path in m1 (2026-10-22, maintainer); the replacement +keeps decoded output timestamps and clean EOF draining, covered with +reordered video. ## Related diff --git a/quest/m2/p2p/cost-scopes.md b/quest/m2/p2p/cost-scopes.md index 056d35cc9a8..a3f6d196361 100644 --- a/quest/m2/p2p/cost-scopes.md +++ b/quest/m2/p2p/cost-scopes.md @@ -22,7 +22,8 @@ Decided 2026-10-13, replacing the unconditional direct-first policy: authorized content when the application turns on the connection's local `proxy` preference (decided 2026-10-14): on for redundancy, such as reaching the CDN through a peer whose uplink died, off for conference offload. - Browser tabs stay off until the [Rust-to-TypeScript](/quest/m2/rs2ts/README.md) port. A neighboring watcher is not a source of its received + Browser tabs never relay, whatever the backend (decided 2026-10-22, + maintainer). A neighboring watcher is not a source of its received media. The authenticated origin identity maps announcements to roster peers. - Matching explicit epochs permit shared logical-track resume. Different or missing epochs require Restart and fresh resolution. Keep CDN control and diff --git a/quest/m2/perf/README.md b/quest/m2/perf/README.md index f126e9b4a69..d36cc87a60a 100644 --- a/quest/m2/perf/README.md +++ b/quest/m2/perf/README.md @@ -9,7 +9,7 @@ and syscalls. Not io_uring specific: anything on the relay's hot path qualifies, including the shared moq-net model layer and kio. Every implementation quest lands with a measured before/after (`just bench BASE` on Linux, -plus the targeted micro-benches it names). A measured no-win is a valid +or `just bench-runtime` for an io_uring change, plus the targeted micro-benches it names). A measured no-win is a valid outcome that abandons the quest. ## Plan @@ -48,10 +48,7 @@ package, and none of the uring micro-opts is measured on `moq-quic`. The unmeasu ones moved out (3200 to m2; 3129 with the open contract, 3201, 3202, and 3204 to m3), and NAPI busy polling (3203), registered wait arguments (3205), and the priority `set_track` wakes were dropped. Egress requeue folded into Run to -quiescence; egress keep-alive folded into Group cost. The benchmark noise -estimate comes first, since every quest here accepts "within noise". The -profiling recipe and One enter per turn rank next: they produce the numbers -that decide the rest. +quiescence. Decided 2026-10-08: the CPU and allocation profile merged into the profiling recipe (`just bench-profile`), so the relay has one recipe with capture modes; #3199 moved to m2 beside 3200, since it is an diff --git a/quest/m2/perf/kio-channel-contention/group-expiry.md b/quest/m2/perf/kio-channel-contention/group-expiry.md index fda09007a60..aae6b33c3f0 100644 --- a/quest/m2/perf/kio-channel-contention/group-expiry.md +++ b/quest/m2/perf/kio-channel-contention/group-expiry.md @@ -9,17 +9,17 @@ no-win abandons it. ## Plan -- `GroupExpiry::is_expired` (`rs/moq-net/src/model/track.rs:3744`) reads the - track state (`self.state.read()`, `track.rs:3760`) on every group poll. +- `GroupExpiry::is_expired` (`rs/moq-net/src/model/track.rs`) reads the + track state (`self.state.read()`) on every group poll. That read is 32% of all mutex wait at 16 workers in both `just bench-profile lock` shapes, per the [epic](/quest/m2/perf/kio-channel-contention/README.md)'s baseline. - Suggested direction: an atomic mirror of the verdict, like `Alive::aborted` - (`rs/moq-net/src/model/group.rs:441`), written under the track lock when the + (`rs/moq-net/src/model/group.rs`), written under the track lock when the verdict can change and read on its own by the group poll. - Open: what the mirror holds. The verdict combines the successor's first frame, the drift edge, the budget, and whether the slot is still cached - (`poll_drifted`, `track.rs:832`), and the read also parks the waiter on + (`poll_drifted`), and the read also parks the waiter on landings and deadlines. Settle the shape first, with options and a recommendation. - Rerun the baseline on a quiet host before the change, since the epic's diff --git a/quest/m2/perf/stream/bench.md b/quest/m2/perf/stream/bench.md index 5c535c9d234..3ed298d816f 100644 --- a/quest/m2/perf/stream/bench.md +++ b/quest/m2/perf/stream/bench.md @@ -22,13 +22,20 @@ and the micro-bench isolates qmux. per message and per Gbps in the same machine-readable form `bench/compare.py` already reads. `just bench-profile` then gives the flamegraph per transport. -- **qmux micro-bench.** A Criterion target in `rs/qmux/benches` over loopback - TCP, swept over sessions × record size (the fan-out rule in `AGENTS.md`), - for tcp, tls, ws, and wss. Use the same CPU-time mode as moq-net's - `session` bench, not wall clock. +- **qmux micro-bench.** A local profiling tool, not a regression series: + [transport sweep](/quest/m2/bench-transport-sweep.md) owns qmux's series + (decided 2026-10-22, maintainer). An example binary in `rs/qmux/examples`, + not a Cargo bench target, since Nightly runs and reports every bench target + (`sh/gh/nightly.test.py`). It drives loopback TCP, swept over sessions × + record size (the fan-out rule in `AGENTS.md`), for tcp, tls, ws, and wss, + and reports process CPU time, not wall clock, so it runs under a profiler. Record the attribution in the epic README: shares for tungstenite, the split/pump/channel/mutex hops, per-record allocations, and TLS. Those shares decide whether the [codec](/quest/m2/perf/stream/codec.md) goes ahead. The [ablation](/quest/m2/perf/stream/ablation.md) adds a ring backend to this same harness. + +## Related + +- [Transport x version benches](/quest/m2/bench-transport-sweep.md) - adds qmux as a transport axis of the moq-net session bench diff --git a/quest/m2/perf/stream/driver.md b/quest/m2/perf/stream/driver.md index 53796fc607f..373835df296 100644 --- a/quest/m2/perf/stream/driver.md +++ b/quest/m2/perf/stream/driver.md @@ -51,3 +51,9 @@ binding that starts a qmux session. The Impact bullet says how to migrate. ## Required - [Bench](/quest/m2/perf/stream/bench.md) - the baseline this is measured against + +## Related + +- [qmux byte counters](/quest/m2/wire-stats/qmux.md) - refreshes `Wire` counters on qmux's read/write path +- [tls:// peers authenticate by certificate](/quest/m2/tls-listener-mtls.md) - changes `qmux::tls::Server::accept` +- [qmux leases receive memory](/quest/m2/qmux-receive-lease.md) - changes qmux's `RecvStream` and window defaults diff --git a/quest/m2/performance-survey/README.md b/quest/m2/performance-survey/README.md index 7691b87c568..f47cd7e5c34 100644 --- a/quest/m2/performance-survey/README.md +++ b/quest/m2/performance-survey/README.md @@ -148,10 +148,11 @@ No duplicate quest or workflow edit belongs here. and paired comparison correctness. Active PRs already address these. - [Perf](/quest/m2/perf/README.md) owns measured channel contention, relay allocations, ingest batching, worker quiescence and runtime selection. -- [Release audit PR](https://github.com/moq-dev/moq/pull/6311) owns QMux - copying/resource growth, CMAF allocation abuse, media reorder queues, - watch teardown, and HLS contract investigations. MKV fragmentation below - concerns parser replay before those queues; recheck that PR before coding. +- The [release audit](/quest/m1/release-audit/README.md) bounded the media + reorder queues in [moq-dev/moq#6453](https://github.com/moq-dev/moq/pull/6453) + and fixed QMux copying in + [moq-dev/moq#6381](https://github.com/moq-dev/moq/pull/6381). MKV + fragmentation below concerns parser replay before those queues. - [Binding bytes](https://github.com/kixelated/rust2/blob/main/quest/m1/ffi-bytes.md), [foreign polling](https://github.com/kixelated/rust2/blob/main/quest/m1/ffi-poll-protocol.md), [Kotlin calls](https://github.com/kixelated/rust2/blob/main/quest/m1/kt-ffi-calls.md), diff --git a/quest/m2/performance-survey/datagram-delivery.md b/quest/m2/performance-survey/datagram-delivery.md index 4a39d457819..65794781fb3 100644 --- a/quest/m2/performance-survey/datagram-delivery.md +++ b/quest/m2/performance-survey/datagram-delivery.md @@ -22,8 +22,8 @@ dropped and expired objects and slow-viewer isolation; best-effort loss must not make lower CPU look like a gain. Keep framing, priority, expiry and memory-accounting behavior intact. The -[release audit](https://github.com/moq-dev/moq/pull/6311) owns the decoded -memory-accounting defect. This quest owns the recurring audit's missing +decoded memory-accounting defect is fixed +([moq-dev/moq#6413](https://github.com/moq-dev/moq/pull/6413)). This quest owns the recurring audit's missing session-level datagram measurement and any independent measured optimization. Follow the [survey's benchmark contract](/quest/m2/performance-survey/README.md): diff --git a/quest/m2/performance-survey/hls-serving.md b/quest/m2/performance-survey/hls-serving.md index d0f56f0efce..7c576e9397d 100644 --- a/quest/m2/performance-survey/hls-serving.md +++ b/quest/m2/performance-survey/hls-serving.md @@ -23,8 +23,8 @@ handover, concurrent first requests and authorization differences. Shared in-flight work or bounded immutable output caching are candidates only after profiling. Keys and ownership must include the actual content instance; never let a reused row number cross epochs or cache one viewer's credentials -in another's playlist. The archive/HLS contract quests and -[release audit](https://github.com/moq-dev/moq/pull/6311) retain policy decisions. +in another's playlist. The archive/HLS contract quests and the +[release audit](/quest/m1/release-audit/README.md) HLS quests retain policy decisions. This takes ownership of the recurring audit's HLS serving coverage item. Follow the [survey's benchmark contract](/quest/m2/performance-survey/README.md): diff --git a/quest/m2/performance-survey/mkv-fragments.md b/quest/m2/performance-survey/mkv-fragments.md index f58c70c87fb..56d3f514f39 100644 --- a/quest/m2/performance-survey/mkv-fragments.md +++ b/quest/m2/performance-survey/mkv-fragments.md @@ -11,9 +11,8 @@ split into smaller reads, without changing accepted media or parse errors. BytesMut, constructs a fresh WebmIterator and skips handled tags only after parsing them (lines 227-252). A B-byte partial block arriving in c-byte chunks can copy O(B²/c) bytes before completion; replay can add parse work. -This is before the reorder queues covered by the -[release audit](https://github.com/moq-dev/moq/pull/6311); recheck its final -scope before implementation. +This is before the reorder queues, which +[moq-dev/moq#6453](https://github.com/moq-dev/moq/pull/6453) bounded. Benchmark block size × input chunk size with identical complete media, including large keyframes, lacing, bounded BlockGroups and unknown-size live diff --git a/quest/m2/qmux-receive-lease.md b/quest/m2/qmux-receive-lease.md index f6aa03eb2b4..ae1376b1e3d 100644 --- a/quest/m2/qmux-receive-lease.md +++ b/quest/m2/qmux-receive-lease.md @@ -11,9 +11,8 @@ chosen after weighing its cost against today's. ## Plan -Starts after [moq-dev/moq#5878](https://github.com/moq-dev/moq/pull/5878) -merges (open on 2026-10-22). Re-read the landed code before designing, since -the lease API may still move. +[moq-dev/moq#5878](https://github.com/moq-dev/moq/pull/5878) merged on +2026-10-22; re-read the landed lease API before designing. State with #5878: qmux's `RecvStream` wraps its reader in moq-net's `poll::Buffered`, which returns connection credit as it reads. `max_data` @@ -62,8 +61,10 @@ Public API: qmux's `RecvStream` leases natively instead of through `Buffered`, and its window defaults or validation change (with a migration note). Wire: QMux MAX_DATA and STOP_SENDING only, no MoQ format change. + ## Related - [Segmented frames](/quest/m1/unbounded-groups/segmented-frames.md) - frame credit returns per segment, shrinking the floor's frame term - [WebSocket caps](/quest/m2/rate-websocket.md) - paced reads on the same sessions, inside the window - [Transport x version benches](/quest/m2/bench-transport-sweep.md) - where a per-session memory and throughput sweep belongs +- [qmux drives its own socket](/quest/m2/perf/stream/driver.md) - replaces qmux's reader and writer tasks and its constructors' return type diff --git a/quest/m2/tls-listener-mtls.md b/quest/m2/tls-listener-mtls.md index 04022c2d1da..bf89741cb99 100644 --- a/quest/m2/tls-listener-mtls.md +++ b/quest/m2/tls-listener-mtls.md @@ -43,3 +43,4 @@ Public API: relay config may gain a listener option. Wire: none. ## Related - [TCP acceptor](/quest/m2/one-port/tcp-demux.md) - carries `tls://` on the shared TCP port, where the certificate request stays on the moq ALPN +- [qmux drives its own socket](/quest/m2/perf/stream/driver.md) - replaces qmux's reader and writer tasks and its constructors' return type diff --git a/quest/m2/wire-stats/qmux.md b/quest/m2/wire-stats/qmux.md index 3dd6ab0f279..4e9bbe6c9ef 100644 --- a/quest/m2/wire-stats/qmux.md +++ b/quest/m2/wire-stats/qmux.md @@ -28,3 +28,6 @@ beside the field. - Test: a loopback WebSocket session's `sent_bytes` covers at least the payload it wrote. +## Required + +- [qmux drives its own socket](/quest/m2/perf/stream/driver.md) - replaces qmux's read/write path, so the counters go on the new driver once (decided 2026-10-22, maintainer)