From 91d058504b28d96561e499604a815df42223899e Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 22 Oct 2026 12:38:56 -0700 Subject: [PATCH 1/4] quest: audit the tree and open PRs for stale and conflicting plans Factual corrections from a full quest audit: drop landed work and dead references, fix moved paths, add missing Required/Related links, and move Drop web-transport-trait above the Perf epic that requires it. Co-Authored-By: Claude Opus 5.5 --- quest/m0/README.md | 3 +-- quest/m0/broadcast-epoch/README.md | 4 ++-- quest/m0/ci/nightly-ffi-reuse.md | 6 ++++-- quest/m1/README.md | 12 ++++-------- quest/m1/api-audit/README.md | 2 +- quest/m1/api-audit/auth-clock-names.md | 5 +++-- quest/m1/api-audit/largest-getter.md | 5 +++-- quest/m1/api-audit/media-trim.md | 1 - quest/m1/api-audit/session-limits.md | 5 +++-- quest/m1/api-audit/transport-config.md | 6 +++--- .../m1/audio-jitter-target/capped-budgets.md | 3 +-- quest/m1/av-clock.md | 1 + quest/m1/lite07-finalize.md | 19 +++++++++---------- quest/m1/public-model/answer.md | 16 ++++++++-------- quest/m1/read-backfill.md | 10 +++++----- quest/m1/refresh/README.md | 2 +- quest/m1/release-perf/sparse-gaps.md | 2 +- quest/m1/resolved-demand.md | 1 + quest/m1/subscribe-ranges/lite.md | 3 +-- quest/m1/test-flakes-2/README.md | 3 --- quest/m1/ts-reliability/README.md | 4 ++-- quest/m2/README.md | 4 ++-- quest/m2/audit-dead-code.md | 7 +++---- quest/m2/audit-docs.md | 2 +- quest/m2/audit-quests.md | 2 +- quest/m2/audit-test-cost.md | 9 ++++----- quest/m2/audit-test-coverage.md | 2 +- quest/m2/bench-transport-sweep.md | 1 + quest/m2/broadcast-fairness.md | 2 +- quest/m2/drop-web-transport-trait.md | 4 ++-- quest/m2/intra-refresh/plan-boundary.md | 6 +++--- quest/m2/perf/README.md | 7 ++----- .../kio-channel-contention/group-expiry.md | 8 ++++---- quest/m2/perf/stream/bench.md | 4 ++++ quest/m2/perf/stream/driver.md | 6 ++++++ quest/m2/performance-survey/README.md | 11 +++++++---- .../performance-survey/datagram-delivery.md | 4 ++-- quest/m2/performance-survey/hls-serving.md | 4 ++-- quest/m2/performance-survey/mkv-fragments.md | 5 ++--- quest/m2/qmux-receive-lease.md | 5 +++++ quest/m2/tls-listener-mtls.md | 1 + quest/m2/wire-stats/qmux.md | 3 +++ 42 files changed, 111 insertions(+), 99 deletions(-) diff --git a/quest/m0/README.md b/quest/m0/README.md index 5c2df85f437..7e948da1d1b 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -18,8 +18,7 @@ done. moq.pro pins this repository's `release` line, so the release gate below also keeps #4741 from reaching it early. 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/broadcast-epoch/README.md b/quest/m0/broadcast-epoch/README.md index defd571a6d6..f604cc60e71 100644 --- a/quest/m0/broadcast-epoch/README.md +++ b/quest/m0/broadcast-epoch/README.md @@ -32,7 +32,7 @@ identity internally; fresh requests cannot join its old cached instance. This does not promise cache invalidation in arbitrary older IETF relays or clients that retain objects across broadcasts. The negotiated [IETF extension](/drafts/draft-lcurley-moq-epoch.md) landed in m1, independently of -this release gate; seamless JS handover stays in m1 too. +this release gate, as did seamless JS handover (#5836). Non-goals: pooling, which needs nothing here. A redundant ingest pair runs on distinct epochs (decided 2026-10-19), so only replicas of one publisher's @@ -114,7 +114,7 @@ handover. Keep existing reads sticky; stopping new unpinned wire work is a separate boundary. Catalog references apply the same identity policy to players and exporters, distinguishing announcement followers from readers that retain lifetime pins. Wall-clock expiry, live DROP dispositions, -FETCH budgets, seamless JS handover, and the IETF extension remain m1. +and FETCH budgets remain m1. Extend the line's relay/media tests to hold catalog and media from A while B replaces it, exercise later metadata/subscription/FETCH operations, and prove diff --git a/quest/m0/ci/nightly-ffi-reuse.md b/quest/m0/ci/nightly-ffi-reuse.md index 574826b84ff..763d6d5e119 100644 --- a/quest/m0/ci/nightly-ffi-reuse.md +++ b/quest/m0/ci/nightly-ffi-reuse.md @@ -15,8 +15,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/m1/README.md b/quest/m1/README.md index 5688f5a2000..209ccb20315 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -47,14 +47,14 @@ before those quests). The moq-transport passthrough fields remain crate-private, the existing explicit exception to the public model audit. -Added 2026-10-16: refresh, a breaking `Demand` change, so it lands before +Added 2026-10-16: refresh, a breaking moq-json change, so it lands before the cut. Added 2026-10-20 (maintainer): the probe target, native probing, and the native rung disable moved up from the [m2 probe epic](/quest/m2/probe/README.md), so Rust, FFI, and OBS publishers recover a stopped rendition in this release; -padding and the browser's rung disable stay in m2, with only padding's -lite-07 stream type here. +padding and the browser's rung disable stay in m2 (padding's lite-07 stream +type landed in [#6233](https://github.com/moq-dev/moq/pull/6233)). ## Required @@ -70,7 +70,7 @@ lite-07 stream type here. - [Generated C bindings](/quest/m1/c/README.md) - C generated from moq-ffi ships as `moq-c` 0.8.0 and replaces the hand-written libmoq - [Exact timestamps in every binding](/quest/m1/binding-timestamps.md) - bindings carry `{value, scale}` timestamps instead of lossy microseconds - [moq-transport passthrough](/quest/m1/ietf-extension/README.md) - object IDs, properties, end status, datagram objects, and unknown track properties survive moq-transport -> moq-relay (and a relay cluster) -> moq-transport; moxygen's moq-test sections 1-7 pass nightly, except cases that use a subgroup other than 0 -- [Refresh](/quest/m1/refresh/README.md) - a join asks for a fresh group once the current group's deltas exceed a publisher-advertised byte ratio, at every hop; replaces `Demand::used`/`unused` and moq-json's `delta_ratio` +- [Refresh](/quest/m1/refresh/README.md) - a join asks for a fresh group once the current group's deltas exceed a publisher-advertised byte ratio, at every hop; replaces moq-json's `delta_ratio` and `op_ratio` - [Announcement takeover](/quest/m1/announce-takeover.md) - a new independent route explicitly continues instances served through its predecessor, with shared Rust/JS semantics and lite-07 wire - [Subscribe ranges](/quest/m1/subscribe-ranges/README.md) - a lite-07 SUBSCRIBE asks for past and live ranges with absolute or relative ends, in either order, and replaces FETCH; merged ranges never starve a subscriber; relays fill misses by range, including over moq-transport - [A widened max age re-offers cached groups](/quest/m1/resolved-demand.md) - a warm relay serves a reader with a wider max age the cached groups a cold relay would, with no wire change @@ -124,7 +124,3 @@ lite-07 stream type here. - [Artifact size audit](/quest/m1/audit-size.md) - binary, FFI, WASM, and bundle growth since the m0 run is explained, fixed, or planned - [Compile time audit](/quest/m1/audit-compile-time.md) - build-time hotspots are measured against the m0 baselines and the worst are fixed or planned - [Performance audit](/quest/m1/audit-perf.md) - benchmarks that regressed since the m0 run are explained, fixed, or planned, and unbenched hot paths get coverage - -## Related - -- [A busy js/net serve yields](https://github.com/moq-dev/moq/pull/5289) - js/net's serve loop leaves the browser its event loop under a fast publisher, if it does not already diff --git a/quest/m1/api-audit/README.md b/quest/m1/api-audit/README.md index 47499d7e397..82b83a164c8 100644 --- a/quest/m1/api-audit/README.md +++ b/quest/m1/api-audit/README.md @@ -82,7 +82,7 @@ Rejected in planning: - [One clean close](/quest/m1/api-audit/clean-close.md) - a clean session close has one spelling and failures are recorded inside moq-net - [Session limits only where needed](/quest/m1/api-audit/session-limits.md) - `session::Limits` keeps only bounds that nothing else enforces, and its documented example compiles - [Ranges refuse what the wire drops](/quest/m1/api-audit/ranges-wire.md) - relative and time anchors fail loud where a wire cannot carry them, `floor()` honors the subscription, and bindings take an optional max delay -- [Derived origins borrow](/quest/m1/api-audit/derived-origin-close.md) - closing a JS `scope()` or `withPreference()` handle releases only that handle +- [Derived origins borrow](/quest/m1/api-audit/derived-origin-close.md) - closing a JS `scope()` handle releases only that handle - [Binding surface trim](/quest/m1/api-audit/binding-trim.md) - route input is separate from route observation, watch verbs are uniform, and unused binding objects are gone - [Wrapper config names](/quest/m1/api-audit/wrapper-config.md) - Python, Go, and Dart client options use the FFI's names and nesting - [One decode read](/quest/m1/api-audit/decode-read.md) - audio and video decoders yield the same `Frame`, `Gap`, `End` read as moq-mux containers diff --git a/quest/m1/api-audit/auth-clock-names.md b/quest/m1/api-audit/auth-clock-names.md index 67761ba24bd..74bc31e3e90 100644 --- a/quest/m1/api-audit/auth-clock-names.md +++ b/quest/m1/api-audit/auth-clock-names.md @@ -17,7 +17,8 @@ Decided 2026-10-22 (API audit): `origin(): Wall` (ms): name both `origin()` and document the unit split. Rust `Manual::install` returns a handle while JS returns a closure: match. - moq-net exposes both `time::now()` and `Instant::now()`; keep one. -- moq-auth's public times (`Claims`, `Grant::expires`) are `SystemTime`; - add them to [wall time](/quest/m1/time/wall.md). +- moq-auth's public times (`Claims`, `Grant::expires`) move from + `SystemTime` to `Wall` in [wall time](/quest/m1/time/wall.md) + ([#6298](https://github.com/moq-dev/moq/pull/6298)); nothing left here. Public API: moq-auth, moq-net, moq-clock, `@moq/clock` renames. Wire: none. diff --git a/quest/m1/api-audit/largest-getter.md b/quest/m1/api-audit/largest-getter.md index a1f5a40a678..04638f4d37d 100644 --- a/quest/m1/api-audit/largest-getter.md +++ b/quest/m1/api-audit/largest-getter.md @@ -10,8 +10,9 @@ JS caller can never hang because another consumed a shared cursor's update. Decided 2026-10-22 (API audit). Rust `track::Producer::largest(&mut self)` (`rs/moq-net/src/model/track.rs`) is a stream on the writer; five production -callers use `poll_largest(&Waiter::noop())` as a getter (moq-mux -`catalog/producer.rs`, `ts/programs.rs`, moq-video `encode/producer.rs`). JS +callers use `poll_largest(&Waiter::noop())` as a getter (moq-cli +`publish.rs`, moq-mux `container/producer.rs` and `container/ts/programs.rs`, +moq-json `snapshot/producer.rs`, moq-video `encode/producer.rs`). JS `Track.Producer.largest()` (`js/net/src/track.ts`) keeps the cursor on the shared handle; reproduced: one caller consumes an update and an independent second caller waits forever. diff --git a/quest/m1/api-audit/media-trim.md b/quest/m1/api-audit/media-trim.md index 4e85c7fc329..ce54b6ed8e1 100644 --- a/quest/m1/api-audit/media-trim.md +++ b/quest/m1/api-audit/media-trim.md @@ -21,7 +21,6 @@ Decided 2026-10-22 (API audit). Make `pub(crate)` or delete: 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-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/session-limits.md b/quest/m1/api-audit/session-limits.md index 56a0e39dfbc..f67137d4646 100644 --- a/quest/m1/api-audit/session-limits.md +++ b/quest/m1/api-audit/session-limits.md @@ -20,8 +20,9 @@ This revisits part of the per-session request caps from #4820 (the m0 DoS review), so prove each removal first: - For each cap, show which existing bound covers it on each version: lite - streams consume QUIC stream credit; drafts 14 to 16 have the request - window; later drafts have MAX_REQUEST_ID. Drop `subscriptions` and + streams consume QUIC stream credit; drafts 14 to 16 have the + MAX_REQUEST_ID request window; draft 17 and later put each request on its + own stream, bounded by QUIC bidi stream credit. Drop `subscriptions` and `fetches` where covered, with a test that the peer is still bounded. - Keep the bounds with no other limit, such as lite announces and the drafts 14 to 16 request window. diff --git a/quest/m1/api-audit/transport-config.md b/quest/m1/api-audit/transport-config.md index 6c00cc90a4f..6bd6007dda1 100644 --- a/quest/m1/api-audit/transport-config.md +++ b/quest/m1/api-audit/transport-config.md @@ -36,9 +36,9 @@ Decided 2026-10-22 (API audit): in a new crate; gate `pub use rustls` on `any(ring, aws-lc-rs)` so it exists under `rustls-aws-lc-rs`; point crate docs at `moq_tokio::quic::driver` instead of quinn. -- 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, moq-quic breaking. Wire: none. +Public API: moq-tokio, moq-quic, 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 0552ec2c011..3b62e1aa065 100644 --- a/quest/m1/audio-jitter-target/capped-budgets.md +++ b/quest/m1/audio-jitter-target/capped-budgets.md @@ -21,8 +21,7 @@ one-second drops as measured. 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/av-clock.md b/quest/m1/av-clock.md index b21dda4d35d..7193fa55771 100644 --- a/quest/m1/av-clock.md +++ b/quest/m1/av-clock.md @@ -62,6 +62,7 @@ Recommendations for the implementation: ## Required +- [Sync.wait says what happened](/quest/m1/api-audit/sync-wait.md) - drops `probe` and `out.jitter` and makes `wait` report present or skip, which the handles build on - [Audio jitter target](/quest/m1/audio-jitter-target/README.md) - the estimator this sits on, and the per-track targets this shape carries ## Related diff --git a/quest/m1/lite07-finalize.md b/quest/m1/lite07-finalize.md index 3ec28c55922..04e27e3fadd 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/public-model/answer.md b/quest/m1/public-model/answer.md index fdafa2a343f..a2534ed5eda 100644 --- a/quest/m1/public-model/answer.md +++ b/quest/m1/public-model/answer.md @@ -74,14 +74,14 @@ Rust items (`rs/moq-net/src/model/`), as they stood at b4eab0db1: (`pub(crate)`, and private in JS) once the identity handle above and the answer source give a peer's route a public shape, and move the out-of-crate tests and benches onto it. -- `origin::interest_prefixes` (both subscribers): remove the need through - Rust lazy discovery ([moq-dev/moq#6165](https://github.com/moq-dev/moq/pull/6165)), which gives Rust - the origin's watched interests the way JS has them; make that public in - both languages there or here, whichever lands second. -- `origin::Consumer::discovery`, `includes_hidden` (IETF publisher): fold - into the public `with_hidden` plus a getter, unless Lazy discovery deletes - them first. `beyond` (topping up a feed): re-inventory after Lazy - discovery; public only if it survives. +- `origin::interest_prefixes` (both subscribers): Rust lazy discovery + ([moq-dev/moq#6165](https://github.com/moq-dev/moq/pull/6165)) landed + with the origin's watched interests still `pub(crate)`, so this quest makes + them public in both languages. +- `origin::Consumer::discovery`, `includes_hidden` (IETF publisher): both + survived Lazy discovery; fold them into the public `with_hidden` plus a + getter. `beyond` (topping up a feed) also survived: re-inventory it, and + make it public only if a consumer needs it. - `origin::Consumer::request` (lite publisher): remove the need; it is the body of the public `request_broadcast`. - `origin::Producer::empty`, `origin::Consumer::empty` (sessions): public. 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/refresh/README.md b/quest/m1/refresh/README.md index a255071319f..c574e402483 100644 --- a/quest/m1/refresh/README.md +++ b/quest/m1/refresh/README.md @@ -67,4 +67,4 @@ Work owned by this README, after the children land: ## Related -- [moq-transport passthrough](/quest/m1/ietf-extension/README.md) - its dynamic-groups child is the IETF half this epic promotes +- [moq-transport passthrough](/quest/m1/ietf-extension/README.md) - its dynamic-groups half landed in [#5708](https://github.com/moq-dev/moq/pull/5708) and is the IETF half this epic promotes diff --git a/quest/m1/release-perf/sparse-gaps.md b/quest/m1/release-perf/sparse-gaps.md index 8b7a74e03d1..b7bbb85f94e 100644 --- a/quest/m1/release-perf/sparse-gaps.md +++ b/quest/m1/release-perf/sparse-gaps.md @@ -11,7 +11,7 @@ faster than their size parameter. Found by reading code on 2026-10-21. Confirm each step with callgrind instruction counts at two sizes before and after the change. -- `Ledger` (`rs/moq-net/src/model/ledger.rs`) stores its runs in a `Vec`. +- `Ledger` (`rs/moq-net/src/ledger.rs`) stores its runs in a `Vec`. [#5802](https://github.com/moq-dev/moq/pull/5802) moved it from the session tail ([#4533](https://github.com/moq-dev/moq/pull/4533), which only ever held a few gaps) onto every track's live arrivals. Each arrival now pays diff --git a/quest/m1/resolved-demand.md b/quest/m1/resolved-demand.md index 0ab1b24a644..8b722c364ca 100644 --- a/quest/m1/resolved-demand.md +++ b/quest/m1/resolved-demand.md @@ -176,3 +176,4 @@ max age or the range. - [Drop set](/quest/m1/drop-set.md) - closes the gap this leaves for groups already reported dropped - [JS unbounded groups](/quest/m2/unbounded-groups/js.md) - the JS byte bound for groups and the cursor runs between them +- [Sparse gaps](/quest/m1/release-perf/sparse-gaps.md) - folds stale gaps into `Delivered`'s floor, which this record replaces; keep `track_gap_flush` and the sparse read benches flat diff --git a/quest/m1/subscribe-ranges/lite.md b/quest/m1/subscribe-ranges/lite.md index 6600a91b9ad..9efcf05d35a 100644 --- a/quest/m1/subscribe-ranges/lite.md +++ b/quest/m1/subscribe-ranges/lite.md @@ -46,8 +46,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/test-flakes-2/README.md b/quest/m1/test-flakes-2/README.md index 4a756d41ddf..290473ea988 100644 --- a/quest/m1/test-flakes-2/README.md +++ b/quest/m1/test-flakes-2/README.md @@ -32,9 +32,6 @@ times on a loaded machine, as the first round did. The redundant failover drift moved to m0 on 2026-10-20 (maintainer) and is fixed by [moq-dev/moq#6113](https://github.com/moq-dev/moq/pull/6113). -Added 2026-10-21 (maintainer): small flakes from the m0/m1 merge run, kept -in one child. - Public API: none. Wire: none. ## Related diff --git a/quest/m1/ts-reliability/README.md b/quest/m1/ts-reliability/README.md index 4d6c81d7df5..1be02660ccd 100644 --- a/quest/m1/ts-reliability/README.md +++ b/quest/m1/ts-reliability/README.md @@ -92,8 +92,8 @@ cause). Out of scope for now: decoupling catalog publication from mux-rate measurement. 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 74762130db0..865558997ad 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -19,7 +19,7 @@ Rate-grant work targets the next unpublished lite version after finalized lite-07; bitrate enforcement is not part of this release. Added 2026-10-13 by the redundant TS alignment plan: TS over RTP legs, -ranked just below deterministic TS legs, which it requires. +ranked just below deterministic TS legs. ## Required @@ -68,6 +68,7 @@ ranked just below deterministic TS legs, which it requires. - [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 - [Group storage benchmark](/quest/m2/group-storage-bench.md) - numbers pick the append-only log, a ring buffer, or sorted runs for `GroupState` before either lands +- [Drop web-transport-trait](/quest/m2/drop-web-transport-trait.md) - every backend implements moq-net's `transport::poll` traits, and the generic `web_transport_trait` adapters are deleted - [Perf](/quest/m2/perf/README.md) - eliminate measured hot-path costs across moq-uring, kio, and the moq-net model - [Performance survey follow-ups](/quest/m2/performance-survey/README.md) - benchmark the source audit's transport, browser, media and serving risks before optimizing - [Nightly benchmark regressions](/quest/m2/bench-ci.md) - each nightly compares the Criterion benches against the previous nightly's commit, publishes it to the git.moq.dev report, and alerts on a regression @@ -132,7 +133,6 @@ ranked just below deterministic TS legs, which it requires. - [JS-only @moq/net](/quest/m2/rs2ts-js.md) - adopt the selected Rust-to-JS route after browser parity and measured tradeoffs are accepted - [Per-stream deadlines](/quest/m2/quic-deadline.md) - hopeless retransmits become resets, and a tail loss probe fires early while there is still time -- [Drop web-transport-trait](/quest/m2/drop-web-transport-trait.md) - every backend implements moq-net's `transport::poll` traits, and the generic `web_transport_trait` adapters are deleted - [Measure ECN on the backbone](/quest/m2/quic-ecn-measure.md) - a written verdict on marking versus dropping, and whether Linode and OVH keep marks - [poll_acked on moq-net's send stream](/quest/m2/quic-ack-hook.md) - the diff --git a/quest/m2/audit-dead-code.md b/quest/m2/audit-dead-code.md index ce466744dbb..321cdebc467 100644 --- a/quest/m2/audit-dead-code.md +++ b/quest/m2/audit-dead-code.md @@ -9,7 +9,7 @@ are planned for deletion when removal breaks a public API. 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). The successor keeps the Goal +milestone's `Required` (m0 to m1, m1 to m2, m2 to m3). 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 @@ -42,9 +42,8 @@ Start by comparing the live tree against these baselines: - Rustc misses unused `pub` items. Tokenizing the tree and keeping `pub` declarations whose name appears exactly once (`.scratch` script, not checked in) found, outside generated bindings: - - cfg(test) helpers `track::Subscriber::assert_error` and - `AnnounceConsumer::assert_next_restarted`/`assert_next_ended` in - moq-net's model, left alone because open PRs churned those files. + - cfg(test) helper `track::Subscriber::assert_error` in moq-net's model, + left alone because open PRs churned that file. - Public items no consumer calls: `moq_net::Version::uses_setup_negotiation`, `moq_tokio::Server::serve_both`, `quic::web` `ClientBuilder::with_server_certificates` and `ServerBuilder::with_certificate`, `hang` `Catalog::to_json_pretty`, diff --git a/quest/m2/audit-docs.md b/quest/m2/audit-docs.md index 54ef0e1bec7..19175c3edc2 100644 --- a/quest/m2/audit-docs.md +++ b/quest/m2/audit-docs.md @@ -9,7 +9,7 @@ undocumented exported symbols are fixed or planned. 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). The successor keeps the Goal +milestone's `Required` (m0 to m1, m1 to m2, m2 to m3). 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-quests.md b/quest/m2/audit-quests.md index 0781b510253..4a742a12ab0 100644 --- a/quest/m2/audit-quests.md +++ b/quest/m2/audit-quests.md @@ -9,7 +9,7 @@ conflicting, and misranked quests are fixed. 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). The successor keeps the Goal +milestone's `Required` (m0 to m1, m1 to m2, m2 to m3). 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 1c8c6beb698..9a6d986afa3 100644 --- a/quest/m2/audit-test-cost.md +++ b/quest/m2/audit-test-cost.md @@ -10,7 +10,7 @@ or planned, so the suite costs what it earns. Workflows and jobs belong to the 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). The successor keeps the Goal +milestone's `Required` (m0 to m1, m1 to m2, m2 to m3). 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 @@ -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/audit-test-coverage.md b/quest/m2/audit-test-coverage.md index 45b76199ca9..5272ae78924 100644 --- a/quest/m2/audit-test-coverage.md +++ b/quest/m2/audit-test-coverage.md @@ -9,7 +9,7 @@ test runs in CI. 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). The successor keeps the Goal +milestone's `Required` (m0 to m1, m1 to m2, m2 to m3). 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/bench-transport-sweep.md b/quest/m2/bench-transport-sweep.md index 7a531f93ea0..fd8a3096d8b 100644 --- a/quest/m2/bench-transport-sweep.md +++ b/quest/m2/bench-transport-sweep.md @@ -23,3 +23,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 ea2b07f2188..d349b71d0d1 100644 --- a/quest/m2/broadcast-fairness.md +++ b/quest/m2/broadcast-fairness.md @@ -84,7 +84,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/drop-web-transport-trait.md b/quest/m2/drop-web-transport-trait.md index 2dfdd635711..940d9654ad6 100644 --- a/quest/m2/drop-web-transport-trait.md +++ b/quest/m2/drop-web-transport-trait.md @@ -3,7 +3,7 @@ ## Goal moq-tokio, moq-uring, moq-relay, moq-ffi and moq-wasm no longer depend on -`web-transport-trait`. The qmux, iroh and wasm backends implement moq-net's +`web-transport-trait`. The iroh and wasm 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 @@ -23,7 +23,7 @@ layer instead of reopening it. - Native QUIC (`moq_tokio::quic::web`, `moq_uring::quic::web`) implements `poll::Session` itself, keeping its real send groups. It is a candidate for the first step, since its `web_transport_trait` impl only feeds the adapter. -- 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/m2/intra-refresh/plan-boundary.md b/quest/m2/intra-refresh/plan-boundary.md index d3f8643c621..df42a920e85 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/perf/README.md b/quest/m2/perf/README.md index c8f8213a3e0..aeb6a48c310 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..6ff3432e2b4 100644 --- a/quest/m2/perf/stream/bench.md +++ b/quest/m2/perf/stream/bench.md @@ -32,3 +32,7 @@ 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 c29a8c0e55f..062521e1fda 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. Add `MIGRATE.md` entries. ## 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 de7cc34cd6c..d8c7976a2cb 100644 --- a/quest/m2/performance-survey/README.md +++ b/quest/m2/performance-survey/README.md @@ -88,10 +88,13 @@ 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) owns QMux copying + ([qmux-copy](/quest/m1/release-audit/qmux-copy.md)), CMAF allocation abuse + ([cmaf-counts](/quest/m1/release-audit/cmaf-counts.md)), media reorder queues + ([export-reorder](/quest/m1/release-audit/export-reorder.md)), watch teardown + ([watch-errors](/quest/m1/release-audit/watch-errors.md)), and the HLS + contract quests. MKV fragmentation below concerns parser replay before those + queues. - [Binding bytes](/quest/m2/ffi-bytes.md), [foreign polling](/quest/m2/ffi-poll-protocol.md), [Kotlin calls](/quest/m2/kt-ffi-calls.md), diff --git a/quest/m2/performance-survey/datagram-delivery.md b/quest/m2/performance-survey/datagram-delivery.md index 4a39d457819..3ca2bb14310 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 +[datagram memory](/quest/m1/release-audit/datagram-memory.md) quest owns the +decoded memory-accounting defect. 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..db77aa2fb35 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 covered by +[export reorder](/quest/m1/release-audit/export-reorder.md). 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 935b59fa059..709277e1e79 100644 --- a/quest/m2/qmux-receive-lease.md +++ b/quest/m2/qmux-receive-lease.md @@ -62,8 +62,13 @@ Public API: qmux's `RecvStream` leases natively instead of through `Buffered`, and its window defaults or validation change (MIGRATE.md). Wire: QMux MAX_DATA and STOP_SENDING only, no MoQ format change. +## Required + +- [Bound incomplete messages](/quest/m1/quic/receive-memory.md) - the receive leases ([#5878](https://github.com/moq-dev/moq/pull/5878)) this extends to qmux + ## Related - [Segmented frames](/quest/m2/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 b7e950ce975..7d2c4a837bb 100644 --- a/quest/m2/tls-listener-mtls.md +++ b/quest/m2/tls-listener-mtls.md @@ -40,3 +40,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..2fa7429faca 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. +## Related + +- [qmux drives its own socket](/quest/m2/perf/stream/driver.md) - replaces qmux's reader and writer tasks and its constructors' return type From 32c509d8ce65fb35e5955dca84edd147fd763309 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 22 Oct 2026 13:40:16 -0700 Subject: [PATCH 2/4] quest: apply the audit's maintainer decisions Fold export reorder into decode order (marked for re-planning), split the OBS decode timing fix into m1, the hang draft warmup amendment and the fMP4 ambisonic layout into their own quests, and record the ordering, ownership, and scope decisions from the audit interview. Co-Authored-By: Claude Opus 5.5 --- quest/m0/README.md | 9 ++--- quest/m0/ci/nightly-ffi-reuse.md | 5 +-- quest/m0/ci/release-gates.md | 5 +-- quest/m1/api-audit/binding-state.md | 4 +++ quest/m1/api-audit/binding-trim.md | 4 +++ quest/m1/api-audit/container-format.md | 4 +++ quest/m1/api-audit/decode-read.md | 4 +++ quest/m1/api-audit/media-trim.md | 8 ++--- quest/m1/api-audit/net-exports.md | 13 ++++--- quest/m1/api-audit/stats-naming.md | 16 +++++---- quest/m1/interop-coverage/README.md | 13 ++++--- quest/m1/release-audit/README.md | 11 +++--- quest/m1/release-audit/export-reorder.md | 36 -------------------- quest/m1/release-audit/obs-decode-timing.md | 24 +++++++++++++ quest/m1/ts-reliability/decode-order.md | 25 ++++++++++++-- quest/m2/README.md | 3 +- quest/m2/audio-encode-mediafoundation.md | 4 +-- quest/m2/bench-transport-sweep.md | 5 ++- quest/m2/intra-refresh/README.md | 3 +- quest/m2/intra-refresh/draft-warmup.md | 24 +++++++++++++ quest/m2/intra-refresh/dual-decoder.md | 4 ++- quest/m2/intra-refresh/plan-boundary.md | 10 +++--- quest/m2/intra-refresh/play-dual-decoder.md | 4 +++ quest/m2/mux-vr-ambisonic-fmp4.md | 23 +++++++++++++ quest/m2/mux-vr-fields.md | 12 +++---- quest/m2/obs-moq-video/source.md | 10 +++--- quest/m2/p2p/cost-scopes.md | 3 +- quest/m2/perf/stream/bench.md | 4 ++- quest/m2/performance-survey/README.md | 2 +- quest/m2/performance-survey/mkv-fragments.md | 2 +- quest/m2/wire-stats/qmux.md | 4 +-- 31 files changed, 196 insertions(+), 102 deletions(-) delete mode 100644 quest/m1/release-audit/export-reorder.md create mode 100644 quest/m1/release-audit/obs-decode-timing.md create mode 100644 quest/m2/intra-refresh/draft-warmup.md create mode 100644 quest/m2/mux-vr-ambisonic-fmp4.md diff --git a/quest/m0/README.md b/quest/m0/README.md index 7e948da1d1b..260c909cd06 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -2,14 +2,11 @@ ## 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. Identity: nothing treats who published a route as what it +The work in flight now. Identity: nothing treats who published a route as what it carries; a path and the epoch on its route are the only content identity, and every first-party publisher that can restart mints a fresh epoch, so #4741 -stalls nobody. Alongside them: fixes for the `release` line, CI cost, and the -recurring audits. +stalls nobody. Alongside it: fixes for the `release` line (including the bounded +CONNECT headers), CI cost, and the recurring audits. ## Plan diff --git a/quest/m0/ci/nightly-ffi-reuse.md b/quest/m0/ci/nightly-ffi-reuse.md index 763d6d5e119..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 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/api-audit/binding-state.md b/quest/m1/api-audit/binding-state.md index 7c5221d14b5..41350e43f8b 100644 --- a/quest/m1/api-audit/binding-state.md +++ b/quest/m1/api-audit/binding-state.md @@ -25,6 +25,10 @@ and Go hide the `Client`, so the first-dial failure is unreachable there. Public API: moq-ffi, moq-c, every wrapper, OBS breaking. 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) + ## Related - [Go first-dial failure](/quest/m2/go-first-dial-failure.md) - becomes unnecessary once the state carries the first failure; delete it when this lands diff --git a/quest/m1/api-audit/binding-trim.md b/quest/m1/api-audit/binding-trim.md index e7991fd211e..34533c53c07 100644 --- a/quest/m1/api-audit/binding-trim.md +++ b/quest/m1/api-audit/binding-trim.md @@ -30,3 +30,7 @@ Decided 2026-10-22 (API audit): it. Align `moq_client_defaults` wrapping across languages. Public API: moq-ffi, moq-c, wrappers breaking. 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/container-format.md b/quest/m1/api-audit/container-format.md index 763b6226c2b..9ac24849c94 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/decode-read.md b/quest/m1/api-audit/decode-read.md index bc6ffdae008..e7ea3e6f053 100644 --- a/quest/m1/api-audit/decode-read.md +++ b/quest/m1/api-audit/decode-read.md @@ -19,6 +19,10 @@ the end while its docs call it a mirror of audio. Public API: moq-audio, moq-video, moq-c breaking. 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) + ## Related - [Consumer warmup](/quest/m2/intra-refresh/consumer-warmup.md) - adjacent decode work diff --git a/quest/m1/api-audit/media-trim.md b/quest/m1/api-audit/media-trim.md index ce54b6ed8e1..c2ee01231cb 100644 --- a/quest/m1/api-audit/media-trim.md +++ b/quest/m1/api-audit/media-trim.md @@ -17,10 +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-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 0e69ac57b7b..6d5323b3614 100644 --- a/quest/m1/api-audit/net-exports.md +++ b/quest/m1/api-audit/net-exports.md @@ -14,13 +14,14 @@ Decided 2026-10-22 (API audit). Keep the items an m1 quest consumes: [routes](/quest/m1/cluster-routing/routes.md)), and `bandwidth::Allocator::set_target` with `transport::Probe` ([native probing](/quest/m1/probe-native.md), -[native rung disable](/quest/m1/simulcast-rung-disable.md)). +[native rung disable](/quest/m1/simulcast-rung-disable.md)), and +`Route::{auth, with_auth}` (documented in `doc/concept/standard.md`, and +[auth issuer](/quest/m1/auth/issuer.md) extends it; decided 2026-10-22, +maintainer). Rust (`rs/moq-net/src/model/`), make `pub(crate)` or delete: -- `Route::{auth, with_auth}` (benches only; reopen with - [in-band auth](/quest/m1/auth/README.md) if it needs them), - `broadcast::Lister` and `Dynamic::lister` (sessions only, and +- `broadcast::Lister` and `Dynamic::lister` (sessions only, and `finish(&self)` does not end it), `stats::Handle::wire`, `cache::Pool::gc` (poll-shaped, not named `poll_*`), `origin::DEFAULT_UPDATE_HOLD` (external uses are test sleeps). @@ -53,3 +54,7 @@ JS (`js/net/src/`): serving "tracks", undocumented `Timed`, `SessionCode`, `StreamCode`. Public API: removals and renames in moq-net and `@moq/net`. Wire: none. + +## Related + +- [Auth issuer](/quest/m1/auth/issuer.md) - extends `Route::auth` across reconnects and reshapes `auth::Handle`, which this quest also trims diff --git a/quest/m1/api-audit/stats-naming.md b/quest/m1/api-audit/stats-naming.md index 094eb5794fd..db0b48cb3e2 100644 --- a/quest/m1/api-audit/stats-naming.md +++ b/quest/m1/api-audit/stats-naming.md @@ -2,9 +2,8 @@ ## Goal -Each moq-stats track and reader has one name that says what it carries, an -encoding is a type rather than a bool, and "stats" means one thing in the -CLI. +Each moq-stats track and reader has one name that says what it carries, +and "stats" means one thing in the CLI. ## Plan @@ -21,9 +20,10 @@ Decided 2026-10-22 (API audit): types while `aggregate::` calls the same readers `*Consumer`; pick one scheme. Fix the doc's "Sessions" heading versus `Presence` and `FrameTooLarge` versus `FRAME_TOO_LARGE`. -- Five `*_track(.., compressed: bool)` functions plus `COMPRESSED_SUFFIX`: - take an `Encoding { Plain, Compressed }`, or nothing once - [compressed tracks](/quest/m1/compress/README.md) removes the suffix. +- The five `*_track(.., compressed: bool)` functions and `COMPRESSED_SUFFIX` + are left to [one compressed track per name](/quest/m1/compress/migrate.md), + which deletes the `.z` siblings; no `Encoding` type (decided 2026-10-22, + maintainer). Rename on top of its single-track functions. - CLI: `import --stats` adds a media report, `export stats` prints it, and `--stats-*` publishes relay `.stats/`, which `export stats` cannot read. Rename the media surfaces `import --report` and `export report` (the code @@ -36,6 +36,10 @@ frame types; note each change in MIGRATE. Public API: moq-stats and moq-cli breaking. Wire: the prefix track name changes. +## Required + +- [One compressed track per name](/quest/m1/compress/migrate.md) - drops the stats `.z` siblings and `TrackPair`, so names change once + ## Related - [Rust stats](/quest/m2/stats/rust.md) - builds on these CLI names diff --git a/quest/m1/interop-coverage/README.md b/quest/m1/interop-coverage/README.md index 4cda43b181f..629ce9af267 100644 --- a/quest/m1/interop-coverage/README.md +++ b/quest/m1/interop-coverage/README.md @@ -12,10 +12,15 @@ 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 once + [SUBSCRIBE_TRACKS outbound](/quest/m1/archive/subscribe-tracks-outbound.md) + ([#6280](https://github.com/moq-dev/moq/pull/6280)) and + [IETF subscribe pause](/quest/m1/ietf-subscribe-pause.md) + ([#6299](https://github.com/moq-dev/moq/pull/6299)) land (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/release-audit/README.md b/quest/m1/release-audit/README.md index 8fc0b2f658a..88ec93744ea 100644 --- a/quest/m1/release-audit/README.md +++ b/quest/m1/release-audit/README.md @@ -74,21 +74,21 @@ Targeted reproductions confirmed a finite HLS startup race, retained HLS 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. Existing range and OBS quests retain ownership -of their findings. Go reservation disposal remains an investigation, and +the reproduction's limits. Existing range quests retain ownership of their +findings; the OBS defects moved to an m1 child (2026-10-22, maintainer). Go reservation disposal remains an investigation, and the security-sensitive HLS admission item is deliberately a public pointer. No platform-wide binding parity, full OBS runtime, fuzzing campaign, or network-impairment performance result is implied by these checks. ## Required -The 19 children comprise 15 reproduced or source-confirmed findings, three +The children comprise reproduced or source-confirmed findings, contract/lifetime investigations, and one security-review pointer. Each child records whether its behavior is new on main or inherited from release. - [Review HLS admission](/quest/m1/release-audit/hls-admission.md) - resolve the security-sensitive resource-boundary review before the cut - [Bound decoded datagram retention](/quest/m1/release-audit/datagram-memory.md) - shared ownership accounts for retained plain and compressed payloads across tracks -- [Bound export reorder storage](/quest/m1/release-audit/export-reorder.md) - flat timestamps cannot bypass MKV/TS backpressure or cause quadratic draining +- [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 - [Validate CMAF counts before allocation](/quest/m1/release-audit/cmaf-counts.md) - tiny fragments cannot force allocations proportional to an untrusted sample count - [Preserve fMP4 sample runs](/quest/m1/release-audit/fmp4-passthrough.md) - passthrough does not substitute gap or interleaved bytes for media samples - [Announce finite HLS imports](/quest/m1/release-audit/hls-startup.md) - a completed valid source cannot race its ready catalog and fail startup @@ -108,8 +108,9 @@ child records whether its behavior is new on main or inherited from release. ## Related +- [Decode order](/quest/m1/ts-reliability/decode-order.md) - owns the export reorder storage finding, folded in on 2026-10-22 - [JS subscription ranges](/quest/m1/subscribe-ranges/js.md) - reproduced datagram bounds failures remain with the existing range implementation owner -- [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 - [Receive memory](/quest/m1/quic/receive-memory.md) - incomplete reliable input remains a separate connection-credit concern - [QPACK dependency fix](/quest/m0/web-transport-proto-0.6.3.md) - decoded header expansion is already tracked - [Compressed tracks](/quest/m1/compress/README.md) - mid-group widening and application migration have existing owners diff --git a/quest/m1/release-audit/export-reorder.md b/quest/m1/release-audit/export-reorder.md deleted file mode 100644 index 5be921c2d62..00000000000 --- a/quest/m1/release-audit/export-reorder.md +++ /dev/null @@ -1,36 +0,0 @@ -# [M] Include decode reordering in exporter backpressure - -## Goal - -MKV and TS export cannot retain unlimited frames or spend quadratic time -draining them when timestamps stop advancing. Preserve valid B-frame timing -while bounding all exporter-owned payloads, including lookahead storage. - -## Plan - -P1, introduced since release by -[moq-dev/moq#5309](https://github.com/moq-dev/moq/pull/5309), with later TS reuse. -At main 6369eca, `rs/moq-mux/src/container/decode.rs:62-93` inserts every frame -in `held` and `slots`, but timestamp-driven `pop` waits for the highest PTS to -advance past lookahead. `MAX_REORDER` bounds media time, not retained bytes. -Compiling this actual source and pushing 100,000 1KiB items at PTS 90,000, -calling `pop(9_000, 0, false)` after each, released zero frames. An explicit -flush recovered all 102,400,000 payload bytes. Flushing also repeatedly scans -remaining slots and removes vector element zero. - -MKV export gates reads on the downstream jitter queue (`mkv/export.rs:351`) -but inserts video into DecodeClock first (`:398-406`); a stuck clock leaves -that queue empty. Catalog jitter or SPS metadata supplies lookahead, and the -normal CLI buffered export defaults to 500ms. MKV ignores group ends. TS has -the same open-group exposure for timestamp-driven clocks (`ts/export.rs:417`, -`:431`, `:798`), although it flushes at later group boundaries and some SPS -paths use a bounded reorder depth. Consumer integration is source-traced; -the reproduction exercised the clock component. - -Tie reorder storage to exporter capacity/ownership, with explicit behavior -for unsupported timestamp progress and without a timer or arbitrary frame -limit. Add exporter-level open-group tests with flat/backward PTS and valid -reordering. Sweep buffered items and payload sizes to measure retained memory -and drain cost. Source-cache accounting cannot solve this downstream queue. -No public API or wire change is expected; report any changed malformed-input -behavior. Current consumers are CLI MKV and TS export. 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..f26a11d7534 --- /dev/null +++ b/quest/m1/release-audit/obs-decode-timing.md @@ -0,0 +1,24 @@ +# [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`), send a flush packet on clean EOF and emit what it returns. Keep the +fix inside the current path; the GPU replacement keeps the same behavior. + +Public API: none. Wire: none. diff --git a/quest/m1/ts-reliability/decode-order.md b/quest/m1/ts-reliability/decode-order.md index cc13d29a786..7a77c3fea25 100644 --- a/quest/m1/ts-reliability/decode-order.md +++ b/quest/m1/ts-reliability/decode-order.md @@ -2,14 +2,35 @@ ## Goal -An exporter that needs a decode time (TS, FLV) derives it only from the -frames of the current group, in the decode order they arrive, and from +An exporter that needs a decode time (TS, MKV, FLV) derives it only from +the frames of the current group, in the decode order they arrive, and from constants fixed for the rendition's life. Two exporters that read the same group author the same DTS for it, whatever they joined at or lost before. Media frames stay presentation-time only: no decode time on the wire. +MKV and TS export cannot retain unlimited frames or spend quadratic time +draining them when timestamps stop advancing: all exporter-owned reorder +storage is bounded by exporter capacity, with valid B-frame timing kept. + ## Plan +Open (2026-10-22, maintainer): the counted approach below is in question +("counts instead of timestamps"). Re-plan it with `/quest-plan` before +starting; the decisions below stand until then. + +Folded in 2026-10-22 (maintainer): the release audit's export reorder +storage quest. `DecodeClock::pop` (`rs/moq-mux/src/container/decode.rs`) +holds every frame in `held` and `slots` until the highest PTS passes the +lookahead, so flat timestamps retain unbounded bytes: 100,000 1 KiB frames +at one PTS released nothing until an explicit flush, and the flush scans +slots and removes element zero each time. MKV export inserts into the clock +before its jitter-queue backpressure (`mkv/export.rs`), and TS has the same +open-group exposure for timestamp-driven clocks. MKV also uses `pop` and +moves with TS. Add exporter-level open-group tests with flat and backward +PTS and valid reordering, and sweep buffered items and payload sizes for +retained memory and drain cost. It was introduced since release by +[#5309](https://github.com/moq-dev/moq/pull/5309), so it gates the cut. + Decided 2026-10-22 by the maintainer, after the TS root-cause audit: - Frames carry PTS only, in decode order. A decoder needs order; a TS diff --git a/quest/m2/README.md b/quest/m2/README.md index 865558997ad..42b4fc13f76 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -65,11 +65,11 @@ ranked just below deterministic TS legs. - [Probe](/quest/m2/probe/README.md) - a session probes up to the bitrate it wants to send or receive, so a stopped simulcast rendition resumes and a viewer up-switches; the browser pads where native sessions retransmit - [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 - [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 - [Group storage benchmark](/quest/m2/group-storage-bench.md) - numbers pick the append-only log, a ring buffer, or sorted runs for `GroupState` before either lands - [Drop web-transport-trait](/quest/m2/drop-web-transport-trait.md) - every backend implements moq-net's `transport::poll` traits, and the generic `web_transport_trait` adapters are deleted - [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 benchmark regressions](/quest/m2/bench-ci.md) - each nightly compares the Criterion benches against the previous nightly's commit, publishes it to the git.moq.dev report, and alerts on a regression - [Relay session bench](/quest/m2/bench-relay.md) - the same scenario through moq-relay's own connection handling @@ -125,6 +125,7 @@ ranked just below deterministic TS legs. - [fMP4 edit lists](/quest/m2/fmp4-edit-lists.md) - the fMP4 importer applies edit lists to frame timestamps - [MKV import refuses out-of-range values](/quest/m2/mkv-dimension-overflow.md) - the Matroska importer errors on a dimension, channel count, or sample rate that would truncate - [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/bench-transport-sweep.md b/quest/m2/bench-transport-sweep.md index fd8a3096d8b..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. diff --git a/quest/m2/intra-refresh/README.md b/quest/m2/intra-refresh/README.md index 08ceca2a431..f97f83a151c 100644 --- a/quest/m2/intra-refresh/README.md +++ b/quest/m2/intra-refresh/README.md @@ -49,10 +49,11 @@ make these presentation guarantees true even when sweep lengths change. ## Required +- [Draft warmup](/quest/m2/intra-refresh/draft-warmup.md) - the hang draft lets a dual-decoder player present min-delay pictures before `warmup` ends - [Join earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - max-delay decoders, native and browser, start a group before the live edge even when the live edge arrives first - [Dual decoder](/quest/m2/intra-refresh/dual-decoder.md) - js/watch runs a min-delay and a max-delay decoder and shows the max-delay picture whenever it has caught up - [Play dual decoder](/quest/m2/intra-refresh/play-dual-decoder.md) - `moq play`'s paced pipeline reuses that pair -- [Plan: recovery boundaries](/quest/m2/intra-refresh/plan-boundary.md) - settle publication ordering and amend the draft so a dual-decoder player may present before `warmup` ends +- [Plan: recovery boundaries](/quest/m2/intra-refresh/plan-boundary.md) - settle publication ordering for a longer sweep, between an exact per-group boundary and a bound published ahead - [Consumer warmup](/quest/m2/intra-refresh/consumer-warmup.md) - the transcoder decodes the groups covering `warmup` as history before each group, except at a true IDR - [H.264 import](/quest/m2/intra-refresh/h264-import.md) - the splitter keeps `recovery_frame_cnt` and import publishes `warmup` from it - [H.265 import](/quest/m2/intra-refresh/h265-import.md) - the splitter reads the recovery-point SEI so an HEVC intra-refresh stream forms groups and publishes `warmup` diff --git a/quest/m2/intra-refresh/draft-warmup.md b/quest/m2/intra-refresh/draft-warmup.md new file mode 100644 index 00000000000..35e7f42e89e --- /dev/null +++ b/quest/m2/intra-refresh/draft-warmup.md @@ -0,0 +1,24 @@ +# [S] The hang draft lets a dual-decoder player present before warmup + +## Goal + +`drafts/draft-lcurley-moq-hang.md`'s `warmup` rule permits a consumer that +runs a parallel max-delay decoder to present min-delay pictures before the +group start plus `warmup` after a non-continuous join. Today the draft says a +consumer MUST NOT, which the merged native pair in `decode::Consumer` +([#5842](https://github.com/moq-dev/moq/pull/5842)) and both player quests +violate. `just drafts check` passes. + +## Plan + +Split from [plan-boundary](/quest/m2/intra-refresh/plan-boundary.md) on +2026-10-22 (maintainer), so the player quests can land on a draft that +allows them without waiting on publication ordering for longer sweeps. + +Keep the MUST for a consumer with a single decoder. Add the dual-decoder +exception: the min-delay pictures MAY be presented while a max-delay decoder +that joined at least `warmup` earlier converges, and the max-delay picture +takes over once its timestamp reaches the min-delay one. Record the change in +the draft's changelog. + +Public API: none. Wire: none (draft text only). diff --git a/quest/m2/intra-refresh/dual-decoder.md b/quest/m2/intra-refresh/dual-decoder.md index 5daa2928ce3..59a716a6a63 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), only on the + draft amendment that permits presenting before `warmup`. - 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 @@ -81,6 +82,7 @@ Decided in the 2026-10-14 planning interview: ## Required +- [Draft warmup](/quest/m2/intra-refresh/draft-warmup.md) - the hang draft permits presenting min-delay pictures before `warmup` ends - [Join earlier group](/quest/m2/intra-refresh/join-earlier-group.md) - the `js/hang` opt-in the max-delay decoder joins with ## Related diff --git a/quest/m2/intra-refresh/plan-boundary.md b/quest/m2/intra-refresh/plan-boundary.md index df42a920e85..721c1bbe0b8 100644 --- a/quest/m2/intra-refresh/plan-boundary.md +++ b/quest/m2/intra-refresh/plan-boundary.md @@ -17,12 +17,10 @@ recall pictures a subscriber has already displayed using the old value. Decided 2026-10-22 (maintainer): precision is settled; the hang draft rounds a fractional-millisecond `warmup` up -([#5212](https://github.com/moq-dev/moq/pull/5212)). This quest amends the -draft's `warmup` rule so a consumer running a parallel max-delay decoder MAY -present min-delay pictures before the group start plus `warmup`, which keeps -the merged dual decoder in `decode::Consumer` -([#5842](https://github.com/moq-dev/moq/pull/5842)). Publication ordering for -a later, longer sweep remains to settle. +([#5212](https://github.com/moq-dev/moq/pull/5212)). The dual-decoder +exception to the `warmup` rule moved to +[draft warmup](/quest/m2/intra-refresh/draft-warmup.md) (2026-10-22, +maintainer). Publication ordering for a later, longer sweep remains to settle. Ask the maintainer to choose between an exact per-group boundary and a safe bound known and published before the affected content, including the latency diff --git a/quest/m2/intra-refresh/play-dual-decoder.md b/quest/m2/intra-refresh/play-dual-decoder.md index 0b2bc6b6474..d8f0fc49908 100644 --- a/quest/m2/intra-refresh/play-dual-decoder.md +++ b/quest/m2/intra-refresh/play-dual-decoder.md @@ -24,3 +24,7 @@ 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. + +## Required + +- [Draft warmup](/quest/m2/intra-refresh/draft-warmup.md) - the hang draft permits presenting min-delay pictures before `warmup` ends 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/stream/bench.md b/quest/m2/perf/stream/bench.md index 6ff3432e2b4..ad0572af3ed 100644 --- a/quest/m2/perf/stream/bench.md +++ b/quest/m2/perf/stream/bench.md @@ -22,7 +22,9 @@ 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 +- **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). 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. diff --git a/quest/m2/performance-survey/README.md b/quest/m2/performance-survey/README.md index d8c7976a2cb..def1ec39fda 100644 --- a/quest/m2/performance-survey/README.md +++ b/quest/m2/performance-survey/README.md @@ -91,7 +91,7 @@ No duplicate quest or workflow edit belongs here. - The [release audit](/quest/m1/release-audit/README.md) owns QMux copying ([qmux-copy](/quest/m1/release-audit/qmux-copy.md)), CMAF allocation abuse ([cmaf-counts](/quest/m1/release-audit/cmaf-counts.md)), media reorder queues - ([export-reorder](/quest/m1/release-audit/export-reorder.md)), watch teardown + ([decode-order](/quest/m1/ts-reliability/decode-order.md)), watch teardown ([watch-errors](/quest/m1/release-audit/watch-errors.md)), and the HLS contract quests. MKV fragmentation below concerns parser replay before those queues. diff --git a/quest/m2/performance-survey/mkv-fragments.md b/quest/m2/performance-survey/mkv-fragments.md index db77aa2fb35..2eaca31978e 100644 --- a/quest/m2/performance-survey/mkv-fragments.md +++ b/quest/m2/performance-survey/mkv-fragments.md @@ -12,7 +12,7 @@ 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 -[export reorder](/quest/m1/release-audit/export-reorder.md). +[decode order](/quest/m1/ts-reliability/decode-order.md). 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/wire-stats/qmux.md b/quest/m2/wire-stats/qmux.md index 2fa7429faca..4e9bbe6c9ef 100644 --- a/quest/m2/wire-stats/qmux.md +++ b/quest/m2/wire-stats/qmux.md @@ -28,6 +28,6 @@ beside the field. - Test: a loopback WebSocket session's `sent_bytes` covers at least the payload it wrote. -## Related +## Required -- [qmux drives its own socket](/quest/m2/perf/stream/driver.md) - replaces qmux's reader and writer tasks and its constructors' return type +- [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) From 02b2cc6e742f417de1357f9c14955aee121a8b8d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 22 Oct 2026 13:42:38 -0700 Subject: [PATCH 3/4] quest: move connection-blocked writers out of the QUIC epic #5878 lands first and retires the QUIC epic with its last child, so the connection-blocked quest from #6363 becomes a top-level m1 quest. Co-Authored-By: Claude Opus 5.5 --- quest/m1/README.md | 1 + .../connection-blocked.md => quic-connection-blocked.md} | 0 quest/m1/quic/README.md | 7 ++++--- 3 files changed, 5 insertions(+), 3 deletions(-) rename quest/m1/{quic/connection-blocked.md => quic-connection-blocked.md} (100%) diff --git a/quest/m1/README.md b/quest/m1/README.md index 3de1d8fa260..07a5f5a8268 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -81,6 +81,7 @@ tracks, so per-group stats stop costing cluster announces. - [Routing guide](/quest/m1/routing-guide.md) - concepts and practical outbound federation setup - [Controlled time](/quest/m1/time/README.md) - every crate and package reads time through moq-clock or @moq/clock, with regression tests for the changed contracts - [Own the QUIC stack](/quest/m1/quic/README.md) - quinn hard-forked in-tree as `moq-quic`, carrying BBR, reliable reset, hierarchical scheduling, peer limits, and endpoint sharding +- [Connection-blocked writers end cleanly](/quest/m1/quic-connection-blocked.md) - an ended stream leaves the blocked-writer list, and STOP_SENDING surfaces under exhausted connection credit - [Rust request credit](/quest/m1/rs-request-credit.md) - a moq-net request fails at once without stream credit instead of waiting - [Generated C bindings](/quest/m1/c/README.md) - C generated from moq-ffi ships as `moq-c` 0.8.0 and replaces the hand-written libmoq - [Exact timestamps in every binding](/quest/m1/binding-timestamps.md) - bindings carry `{value, scale}` timestamps instead of lossy microseconds diff --git a/quest/m1/quic/connection-blocked.md b/quest/m1/quic-connection-blocked.md similarity index 100% rename from quest/m1/quic/connection-blocked.md rename to quest/m1/quic-connection-blocked.md diff --git a/quest/m1/quic/README.md b/quest/m1/quic/README.md index 474050188b0..0d8bf0606c1 100644 --- a/quest/m1/quic/README.md +++ b/quest/m1/quic/README.md @@ -8,8 +8,10 @@ MoQ never negotiates. One core serves the tokio backend, the thread-per-core `moq-uring` backend, and qmux; iroh stays on upstream noq. BBR correctness, reliable stream resets (#5690), hierarchical send groups (#5711, #5770), one `Limits` type for relay peers (#5871), and endpoint sharding (#5688) are on -`main`. What remains is bounded incomplete-message memory and clean exits -for connection-blocked writers; per-broadcast +`main`. What remains is bounded incomplete-message memory; clean exits for +connection-blocked writers moved to +[their own quest](/quest/m1/quic-connection-blocked.md) so they outlive this +epic (2026-10-22). Per-broadcast fairness gives the crate-private send domains (#5872) their consumer in [Broadcast fairness](/quest/m2/broadcast-fairness.md). qmux already runs on the shared stream state machine, outside this epic. @@ -72,7 +74,6 @@ cache limits remain separate; bitrate pacing composes with this memory cap. ## Required - [Bound message assembly](/quest/m1/quic/receive-memory.md) - complete headers and frames return credit, and receiver priority cancellation restores progress -- [Connection-blocked writers end cleanly](/quest/m1/quic/connection-blocked.md) - an ended stream leaves the blocked-writer list, and STOP_SENDING surfaces under exhausted connection credit ## Related From 842e43e23b1dd5298b3605a7407ecf45644602a1 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Thu, 22 Oct 2026 13:39:24 -0700 Subject: [PATCH 4/4] chore(quest): point audit quests at their owners Peer count bounds links the session-limits quest that keeps or drops the per-session caps, and the TS program cap waits for pat-lifecycle, which rewrites programs.rs. Co-Authored-By: Claude Opus 5.5 --- quest/m1/peer-count-bounds.md | 2 +- quest/m1/ts-reliability/program-cap.md | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/quest/m1/peer-count-bounds.md b/quest/m1/peer-count-bounds.md index ff44d5f8c92..09f1afa469d 100644 --- a/quest/m1/peer-count-bounds.md +++ b/quest/m1/peer-count-bounds.md @@ -28,4 +28,4 @@ Public API: none. Wire: none. ## Related - [In-band auth](/quest/m1/auth/README.md) - the `AUTH_OK` decoders this area started from -- [Relay session limits](/quest/m0/README.md) - the existing per-session request caps these follow +- [Session limits](/quest/m1/api-audit/session-limits.md) - keeps or drops the per-session request caps these sit beside diff --git a/quest/m1/ts-reliability/program-cap.md b/quest/m1/ts-reliability/program-cap.md index b8bd4b429e8..cd9398119cf 100644 --- a/quest/m1/ts-reliability/program-cap.md +++ b/quest/m1/ts-reliability/program-cap.md @@ -14,10 +14,15 @@ Decided 2026-10-22 from the security audit, with the maintainer: - No new program-count constant or run budget: a cap below the PAT's domain would only refuse valid multiplexes. The session announce budget does not apply here, since it counts what a remote peer announces to us, not broadcasts `Programs` creates locally. - Test with a synthesized PAT naming far more programs than it carries PMTs for, and assert only the described programs exist. +This starts after [PAT changes](/quest/m1/ts-reliability/pat-lifecycle.md) lands, since that quest rewrites `programs.rs`. + Public API: none. Wire: none. +## Required + +- [PAT changes](/quest/m1/ts-reliability/pat-lifecycle.md) - rewrites `programs.rs`, the PAT handling this changes + ## Related - [TS fidelity and recovery](/quest/m1/ts-reliability/README.md) - the TS import contract this belongs to -- [PMT lifecycle](/quest/m1/ts-reliability/pat-lifecycle.md) - the PAT handling beside it - [Shared TS parser](/quest/m1/ts-reliability/shared-parser.md) - one parser for every program, so per-chunk cost does not grow with the programs this quest admits