Skip to content
3 changes: 3 additions & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ tracks, so per-group stats stop costing cluster announces.
- [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
- [Binding encoder config](/quest/m1/binding-encoder-config.md) - binding encoders take an exact rational framerate, and the publication track name moves to options only the producers take
- [FFI shutdown requests the drain synchronously](/quest/m1/ffi-shutdown-sync.md) - `shutdown()` becomes a sync drain request awaited with `closed()`, so a dropped future can no longer cancel
- [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`
- [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
Expand Down Expand Up @@ -139,6 +140,8 @@ tracks, so per-group stats stop costing cluster announces.
- [NixOS leaf dials its root](/quest/m1/nix-cluster-connect.md) - the NixOS relay module sets the cluster variable the relay reads, so a leaf joins its root instead of running standalone
- [Wayland display id](/quest/m1/wayland-display-id.md) - a requested display id off X11 is refused or honored, never silently replaced by a portal pick shared across publications
- [CI housekeeping](/quest/m1/ci-housekeeping.md) - validate queue nextest at 24 threads and remove the actionlint queue ignore after its release
- [Nightly OBS TSan suite](/quest/m1/obs-test-nightly.md) - Nightly runs `just obs test` so a broken suite is seen
- [libav polyfill fork fix](/quest/m1/libav-polyfill-fork.md) - our polyfill fork copies planar audio correctly and `@moq/hang` drops its patch
- [Wire-compat audio](/quest/m1/wire-compat-audio.md) - the wire-compat media lanes decode and check the interop tone across releases
- [Interop AAC](/quest/m1/interop-aac.md) - the Rust interop publisher adds an AAC tone and every subscriber checks it beside Opus
- [Interop pre-skip](/quest/m1/interop-preskip.md) - the interop tone check anchors phase to timestamps, so a misapplied pre-skip fails
Expand Down
41 changes: 41 additions & 0 deletions quest/m1/ffi-shutdown-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# [M] FFI shutdown requests the drain synchronously

## Goal

`MoqSession::shutdown()` in moq-ffi is a plain (non-async) call that requests
the draining close before it returns, like `cancel(code)`. A caller waits for
the drain with the existing async `closed()`. Dropping or never polling a
future can no longer turn a shutdown into a cancel. Every binding and wrapper
follows, with a `MIGRATE.md` entry in each affected package.

## Plan

Today `shutdown` is async and runs the close through `ffi::detached`
(`rs/moq-ffi/src/ffi.rs`), which aborts its task when the caller drops the
future. The async body only runs on first poll, so a caller that drops the
future early, as OBS's `MoQOutput::Stop` must avoid by holding it, gets a cancel
instead of a drain (found in [moq-dev/moq#6277](https://github.com/moq-dev/moq/pull/6277)).

Decided 2026-10-22 (maintainer): a synchronous `shutdown()` that only requests
the drain, waited on with `closed()`. Rejected: keeping it async and never
aborting the close (no API change, but the request still waits for a poll), and
an extra `begin_shutdown()` beside the async one (two methods for one action).

- Start the close on the FFI runtime before `shutdown()` returns, and report a
close that fails through `closed()`.
- Follow the Cross-Package Sync row for `rs/moq-ffi`: `rs/moq-c` (or the
generated C package, whichever ships first), the py, swift, kt, dart, and go
wrappers, `cpp/moq`, `cpp/obs/src` (drop the "keep the future" workaround in
`MoQOutput::Stop`), and `doc/lib/*`.
- Test: a shutdown whose result is never awaited still drains a finished track,
with mocked time, in moq-ffi and through the OBS output test.
- The `release` line's additive async `drain()`
([FFI shutdown backport](/quest/m0/ffi-shutdown-backport.md)) maps onto this
at the next breaking cut; say so in each MIGRATE entry.

Public API: breaking in moq-ffi and every binding. Wire: none.

## Related

- [FFI shutdown backport](/quest/m0/ffi-shutdown-backport.md) - the release line's async `drain()`
- [MIGRATE matches release](/quest/m1/api-audit/migrate.md) - plans entries saying `shutdown` became an async `Result`; they must describe the final sync shape instead
1 change: 1 addition & 0 deletions quest/m1/interop-coverage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ coverage on `main` at `3ab6cbf81d`.

## Related

- [Runner client bump](/quest/m2/release/runner-client-bump.md) - after the cut publishes #5083, the runner's Rust client stops stamping every object
- [Interop audit](/quest/m0/audit-interop.md) - classifies the red cells these cases produce
- [moq-transport passthrough](/quest/m1/ietf-extension/README.md) - Moxygen's moq-test conformance through moq-relay
19 changes: 19 additions & 0 deletions quest/m1/libav-polyfill-fork.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# [XS] Fix the libav polyfill's planar copy in our fork

## Goal

The `@kixelated` fork of libavjs-webcodecs-polyfill converts interleaved
samples to planar from the right offset, a release of it is published, and
`@moq/hang` drops the copy patch that works around it.

## Plan

Found by [moq-dev/moq#6284](https://github.com/moq-dev/moq/pull/6284): the
polyfill garbles every channel after the first, so stereo Opus under Node, Bun,
or a browser without `AudioDecoder` lost its right channel. #6284 patches the
conversion in `Util.Libav.polyfill()` with a regression test; keep that test
when the patch goes.

Decided 2026-10-22 (maintainer): fix it in the fork only; post nothing upstream.

Public API: none. Wire: none.
17 changes: 17 additions & 0 deletions quest/m1/obs-test-nightly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# [XS] Nightly runs the OBS TSan suite

## Goal

A Nightly job runs `just obs test` (the OBS plugin's ThreadSanitizer suite)
and its failure is visible like any other Nightly lane.

## Plan

Nothing ran it, so it failed every time on `main` unnoticed until
[moq-dev/moq#6277](https://github.com/moq-dev/moq/pull/6277) fixed its
suppression rule. Add it to the Nightly recipe matrix in
`.github/workflows/nightly.yml` with the `.#obs` shell it needs, and record its
runtime; a loaded TSan run of `moq-output-test` can take 60 to 90 s instead of
about 8.

Public API: none. Wire: none.
7 changes: 6 additions & 1 deletion quest/m1/test-flakes-2/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# [S] More tests hold up under load
# More tests hold up under load

## Goal

Expand Down Expand Up @@ -37,6 +37,11 @@ in one child.

Public API: none. Wire: none.

## Required

- [Media resource instrumentation](/quest/m1/test-flakes-2/resource-instrumentation.md) - the media test stops seeing a playing player report nothing to release
- [Interop cpp to js budget](/quest/m1/test-flakes-2/interop-cpp-js.md) - the cpp -> js cell finishes inside its 30 s budget, fixed at its cause

## Related

- [Controlled time](/quest/m1/time/README.md) - the systemic fix these rules ask for: every crate on a controlled clock
Expand Down
15 changes: 15 additions & 0 deletions quest/m1/test-flakes-2/interop-cpp-js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# [S] The interop matrix's cpp to js cell stays inside its budget

## Goal

The `cpp -> js` cell of `just test interop --all` finishes inside its 30 s
budget on CI, fixed at its cause rather than by raising the budget.

## Plan

It took 38 s once in a CI Interop dispatch while working
[moq-dev/moq#6302](https://github.com/moq-dev/moq/pull/6302) (run 39734065658).
Find which phase overran (C++ publisher start, the browser's first frame, or
teardown) from that run's log first, then reproduce under load.

Public API: none expected. Wire: none.
19 changes: 19 additions & 0 deletions quest/m1/test-flakes-2/resource-instrumentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# [S] The media test's resource instrumentation reports what the player holds

## Goal

The media interop test's resource instrumentation check passes reliably under
a loaded `just check`, fixed at its cause. It sometimes fails with the player
reporting nothing to release while it is still playing.

## Plan

Seen twice locally while working
[moq-dev/moq#6302](https://github.com/moq-dev/moq/pull/6302), once with the
fixture's mono pin and once without, so the pin is unrelated. Reproduce under
load with the diagnostics #6302 added (a failed media window prints each page's
long animation frames and the host's busiest processes), then decide whether
the player drops its resource handles early or the check samples before they
exist. Never widen the check's wait to hide it.

Public API: none expected. Wire: none.
1 change: 1 addition & 0 deletions quest/m2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ ranked just below deterministic TS legs, which it requires.
- [Android capture](/quest/m2/mobile-capture-android.md) - Rust captures through NDK/JNI on Android, reusing the existing codecs and setting the catalog rotation
- [Time stretch](/quest/m2/watch-audio-time-stretch.md) - js/watch: the audio ring converges by time-stretching instead of skipping or going silent
- [Audio join budget](/quest/m2/audio-join-budget.md) - `moq-audio` joining at the latest group loses no group a publisher bursts before its budget widens
- [Latest readers use Range::live()](/quest/m2/readers-range-live.md) - after #6278, readers start at the live edge without waiting on `largest()`
- [Media QA audio cases](/quest/m2/media-qa-audio.md) - AAC, audio codec/rate/channel changes, rendition switches, output mute/volume, and a native publisher's A/V sync are graded like the video cases
- [Audio outlives video](/quest/m2/audio-bufferbloat-av.md) - under bufferbloat audio stays within budget while video may skip, rendered frames stay in sync, and native audio rows are enforced
- [ViSQOL grading](/quest/m2/audio-visqol.md) - the nightly scores played audio against a reference clip and fails below measured floors
Expand Down
32 changes: 32 additions & 0 deletions quest/m2/readers-range-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# [S] Latest readers subscribe with a relative range

## Goal

Condition: [moq-dev/moq#6278](https://github.com/moq-dev/moq/pull/6278) has
merged, so `Range::live()` starts at the live edge on a warm relay copy too.

Then the readers that wait for the largest group and raise their floor to
start near it (moq-audio, moq-video, `moq play`, and both moq-room and
`@moq/room` chat) subscribe with a relative range instead, with no change in
what they play: `Range::live()` for a start at the newest group, and a
relative start for a reader that looks back, such as moq-video's warmup lane,
which keeps its one-group lookback so the preceding intra-refresh group still
decodes.

## Plan

Inventory with `rg 'largest\(|poll_largest' rs/ js/` first, since Rust chat
polls rather than awaits; keep any caller that needs the position itself
rather than a start. Each reader keeps its existing tests,
plus one against a warm relay copy.

Public API: none expected. Wire: none.

## Required

- [JS ranges](/quest/m1/subscribe-ranges/js.md) - `@moq/room` chat needs relative ranges in `@moq/net`

## Related

- [Audio join budget](/quest/m2/audio-join-budget.md) - moq-audio's join at the latest group
- [Track live signal](/quest/m1/track-live.md) - the fix this builds on
1 change: 1 addition & 0 deletions quest/m2/release/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ production stability proven merely because upstream artifacts published.
- [Retire hand-written C](/quest/m2/release/c-retire.md) - delete the hand-written crate once generated C publishes
- [Retire the web-transport JS qmux](/quest/m2/release/qmux-js-retire.md) - once `@moq/qmux` publishes from `js/qmux`, deprecate `@moq/web-socket-stream` and stop the second publisher
- [moq.sh deployment](/quest/m2/release/moq-sh-deploy.md) - verify its release-triggered publication
- [Runner client bump](/quest/m2/release/runner-client-bump.md) - the upstream interop runner's Rust client moves to the published moq-tokio, so it stops stamping every object
23 changes: 23 additions & 0 deletions quest/m2/release/runner-client-bump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# [XS] The runner's Rust client stops stamping every object

## Goal

Condition: a moq-tokio release carries
[moq-dev/moq#5083](https://github.com/moq-dev/moq/pull/5083), a breaking
change on `main` that publishes only with the next cut. Check with
`cargo info moq-tokio` and that release's changelog.

Then bump the moq-dev-rs client in
[englishm/moq-interop-runner](https://github.com/englishm/moq-interop-runner)
to it, so it stops attaching a Timestamp property to every object, and delete
this quest once that PR merges.

## Plan

The released client adds the property, which third-party subscribers count as
an unexpected extension in `data-subgroup-basic`. The interop audit's standing
approval covers PRs touching the `moq-dev-*` builds.

## Related

- [Runner cases](/quest/m1/interop-coverage/runner-cases.md) - the cases this keeps green
Loading