Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bcfb12c
quest: plan the probe chain move, evicted-group staleness, and #5878/…
kixelated Oct 20, 2026
9731713
quest: move native probing and the failover drift flake, list the new…
kixelated Oct 20, 2026
82b4452
quest: bound what a peer's probe target costs by native's overhead bu…
kixelated Oct 20, 2026
3820f95
quest: refuse spatial input the catalog cannot describe
kixelated Oct 20, 2026
98f9435
quest: widen a mid-group compressed subscription at the subscriber
kixelated Oct 20, 2026
2bc6afe
quest: address review on spatial refusals, rung floors, and test cove…
kixelated Oct 20, 2026
a51a0a0
Merge remote-tracking branch 'origin/main' into quest/plan-probe-qmux…
kixelated Oct 20, 2026
9f38bae
quest: gate quic-qmux on the moq-gst close hangs and require it from …
kixelated Oct 20, 2026
0823b19
quest: split viewer targets from the m1 send target
kixelated Oct 21, 2026
ab943b0
Merge remote-tracking branch 'origin/main' into quest/plan-probe-qmux…
kixelated Oct 21, 2026
c51d285
Merge branch 'quest/plan-probe-qmux-followups' into quest/plan-probe-…
kixelated Oct 21, 2026
50c99f8
quest: address the #6102 merge review on padding versions, probe expi…
kixelated Oct 21, 2026
ca7c454
Merge remote-tracking branch 'origin/main' into quest/plan-probe-qmux…
kixelated Oct 21, 2026
b2a1dbb
Merge branch 'quest/plan-probe-qmux-followups' into quest/plan-probe-…
kixelated Oct 21, 2026
8d0c53f
Merge remote-tracking branch 'origin/main' into quest/plan-probe-qmux…
kixelated Oct 21, 2026
868a757
Merge branch 'quest/plan-probe-qmux-followups' into quest/plan-probe-…
kixelated Oct 21, 2026
c4a8662
Merge remote-tracking branch 'origin/main' into quest/plan-probe-qmux…
kixelated Oct 21, 2026
e0076b1
quest: drop links to quests #6141 and #5849 completed
kixelated Oct 21, 2026
3202814
quest: address review on the JS send target, VR pose refusals, and re…
kixelated Oct 21, 2026
6e33aa6
Merge remote-tracking branch 'origin/main' into quest/plan-probe-qmux…
kixelated Oct 21, 2026
ba641f7
Merge origin/main into quest/plan-probe-qmux-followups
kixelated Oct 21, 2026
d40c37c
Merge origin/main into quest/plan-probe-qmux-followups
kixelated Oct 21, 2026
591201e
Merge origin/main into quest/plan-probe-qmux-followups
kixelated Oct 21, 2026
3667e58
quest: keep subscribe pinned unless its output marks a restart
kixelated Oct 21, 2026
7a92a4e
Merge origin/main into quest/plan-probe-qmux-followups
kixelated Oct 22, 2026
1272c86
quest: address review on restart failure, replica order, receive fair…
kixelated Oct 22, 2026
f3cd70d
quest: rung disable needs a sender, so iroh and CUBIC sessions keep e…
kixelated Oct 22, 2026
0e6a252
quest: native probing requires evicted-group staleness; name which na…
kixelated Oct 22, 2026
e21672f
quest: VR importers refuse stereo layouts the catalog cannot represent
kixelated Oct 22, 2026
a041f48
quest: schedule evicted-group staleness before native probing
kixelated Oct 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions quest/m0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ the runner moved here from Watch (maintainer, 2026-10-13). The other red jobs
have fixes on `main`: macOS tests (#5335), drill-sensitivity (#5338), loom
(#5389) and Windows tests (#5339).

Flakes (2026-10-20, maintainer): the redundant failover drift moved here from
the m1 [load round](/quest/m1/test-flakes-2/README.md). It fails loaded
`just check` runs, the local gate every merge waits on, and may hide a TS
export bug.

Hygiene (decided 2026-10-19): the audits sit at the end of this list.
Each recurs once per major release: its PR adds the next run to the following
milestone, gated from m2 onward on the [release cut](/quest/m2/release/cut.md).
Expand All @@ -83,6 +88,7 @@ Each lands its fixes in its own PR and plans only what needs a decision.
## Required

- [CI cost and latency](/quest/m0/ci/README.md) - cloud CI runs only in the merge queue as one `just ci` pass, expensive checks run nightly and gate `release`, caches move to R2, and runners are chosen last
- [Redundant failover drift](/quest/m0/redundant-failover-drift.md) - a stitching TS export switches back to the surviving importer behind bufferbloat instead of failing on a stalled clock
- [qmux 0.5.3](/quest/m0/qmux-0.5.3.md) - crates.io publishes qmux 0.5.3 with the builder session config the release backport of #5609 needs
- [Release shutdown backport](/quest/m0/ffi-shutdown-backport.md) - `release` publishes a non-breaking moq-ffi with an async `drain()` and qmux sessions on the QUIC stream limit
- [Release fixed delay](/quest/m0/release-fixed-delay.md) - a fixed latency on `release`, which moq.dev runs, ignores the catalog's jitter and delay as `main` already does
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ slowest test in the queue suite (37 s, past nextest's 30 s SLOW mark).
## Plan

Found by the 2026-10-19 test cost audit, with the exporter's stderr captured
on a failing run. After the test kills the newest importer, the exporter
on a failing run. Moved from the m1
[load round](/quest/m1/test-flakes-2/README.md) to m0 on 2026-10-20
(maintainer), since it fails loaded `just check` runs. After the test kills the newest importer, the exporter
fails instead of switching back to the survivor:

> export failed with the broadcast still up, so not lingering ... the
Expand Down
21 changes: 18 additions & 3 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,29 @@ Added 2026-10-16: refresh, ranked after moq-transport passthrough, whose
dynamic-groups path it promotes. It is a breaking `Demand` 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.

## Required

- [Epoch correctness](/quest/m0/broadcast-epoch/README.md) - publisher identity, duplicate suppression, and catalog pinning gate the breaking cut

- [Sink restart follow](/quest/m1/sink-restart-follow.md) - moq-cli's server and push sinks follow an epochless `Restart` where their output can mark it, and fail loud otherwise
- [Readers follow restarts](/quest/m1/sink-restart-follow.md) - every moq-cli and moq-gst reader, moqsrc and `moq play` included, follows instances through `announced_broadcast` and an epochless `Restart` where its output can mark it; otherwise it stays on the old instance and fails once that route dies
- [Cluster routing](/quest/m1/cluster-routing/README.md) - prove shared routing contracts, implement per-origin routes, and connect outbound local clusters
- [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
- [qmux on the QUIC stream state machine](/quest/m1/quic-qmux.md) - qmux runs in-tree over `moq-quic`'s stream state machine, returning credit for every byte and delivering its close frame
- [qmux on the QUIC stream state machine](/quest/m1/quic-qmux.md) - qmux runs in-tree over `moq-quic`'s stream state machine, returning credit for every byte, delivering its close frame, and ending an unfinished read after its buffered data once the session closes
- [lite-07 padding](/quest/m1/lite07-padding.md) - lite-07 reserves a padding stream type that receivers read and discard, for the browser's padding probes
- [Finalize moq-lite-07](/quest/m1/lite07-finalize.md) - when the maintainer cuts it, lite-07 negotiates as `moq-lite-07` and the next release ships it
- [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`
- [Compressed tracks](/quest/m1/compress/README.md) - a track flagged `compress` is DEFLATEd per group by moq-net, forwarded compressed, and inflated once on the relay for peers that cannot signal it; `catalog.json.z` and moq-flate go away
- [Moxygen minimal interop](/quest/m1/smoke-moxygen.md) - waiting on Moxygen to send a registered filter type, then the smoke lane stops hiding its failure
- [IETF setup waits under validity](/quest/m1/ietf-setup-validity.md) - every IETF subscriber setup wait ends once its request's route retires or its grant is revoked
- [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
- [More tests under load](/quest/m1/test-flakes-2/README.md) - the second round of load-only failures, one quest per flake, fixed at the cause
- [A public model](/quest/m1/public-model/README.md) - every model item a built-in session reaches is public, unnecessary, or marked deliberately private, so a custom publisher, subscriber, or relay builds from the public model alone
Expand All @@ -77,10 +85,15 @@ lands before the cut.
- [Identity and recovery guide](/quest/m1/identity-guide.md) - explain pinned catalogs, lazy jobs, takeover, restart and version compatibility
- [Reference epoch bindings](/quest/m1/reference-epoch-bindings.md) - the UniFFI bindings and C ABI expose and honor a catalog reference's optional `epoch`
- [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
- [Scope track priority](/quest/m1/track-priority-scope.md) - priority orders one owner's streams, and a shared cluster session is fair across tenants
- [Scope track priority](/quest/m1/track-priority-scope.md) - priority orders one owner's streams, sent and received, and a shared cluster session is fair across tenants
- [Session send target](/quest/m1/probe-target.md) - a session probes up to the sum of its publisher reservations through whichever sender its transport has
- [An evicted group is judged stale](/quest/m1/evicted-group-stale.md) - a handed-out group the cache evicted goes stale once its successor's arrival plus the budget passes, so its stalled stream resets
- [Native probing](/quest/m1/probe-native.md) - moq-quic retransmits in-flight data during BBR3's ProbeBW_UP so an app-limited sender validates headroom up to the target

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Schedule evicted-group staleness before native probing

Fresh evidence after the Required-link fix is the milestone schedule: native probing remains here at line 90, while its new evicted-group-staleness prerequisite is not scheduled until line 105, contrary to this README's prerequisite-first ordering. Executing m1 in this order therefore still ships probing while an evicted group cannot be expired, allowing ProbeBW_UP to retransmit its stale in-flight bytes; move the staleness quest ahead of this entry.

AGENTS.md reference: AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, fixed in a041f48: the staleness quest now sits right before native probing in m1.

(Written by Claude Opus 5.5)

- [Native rung disable](/quest/m1/simulcast-rung-disable.md) - Rust/FFI and OBS publishers stop encoding the top renditions their grant cannot fund and resume them once probing raises the estimate
- [Audio jitter target](/quest/m1/audio-jitter-target/README.md) - the playout target is default in both languages; a manual browser proof and a native trace replay remain
- [A/V clock](/quest/m1/av-clock.md) - the audio playhead drives Sync.reference while audio plays, through per-track sync handles
- [Publish channel count](/quest/m1/publish-audio-channel-count.md) - forcing a channel count on an Audio.Capture stops costing the subscriber gaps of silence, and the media fixture's cold-start audio-tone flake is fixed at its cause
- [moq --listen admission test](/quest/m1/listen-admission-test.md) - an in-process test proves `moq --listen` bounds admission by `listen.timeout`
- [Relay signal install](/quest/m1/relay-signal-install.md) - a shutdown signal after the relay starts serving drains it, without leaking handlers into an embedder whose startup failed
- [JS redirect keys strip userinfo](/quest/m1/js-reload-userinfo.md) - js/net keys a reconnect endpoint without URL userinfo, matching Rust
- [Container reads drop backfill](/quest/m1/read-backfill.md) - the JS container consumer drops groups below its read cursor like Rust
Expand All @@ -91,7 +104,9 @@ lands before the cut.
- [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
- [Drop set](/quest/m1/drop-set.md) - each cursor judges missing groups against its own budget, so a narrow reader drops promptly and a widening re-offers groups already reported dropped on lite-07
- [Resumed close](/quest/m1/resume-closed.md) - a resumed JS track keeps its declared end across copies, and its `closed` and `largest()` settle on a clean end
- [Replica resume](/quest/m1/replica-resume.md) - a lagging same-epoch replica resumes with every frame exactly once and in order, on lite-07 and on moq-transport
- [Resumed datagrams](/quest/m1/resume-datagrams.md) - a resumed track skips only datagrams it already delivered, not every lower sequence
- [Recovery verdicts](/quest/m1/recovery-verdicts.md) - group recovery refetches only on errors a new copy can fix, so a repeating error ends the group instead of looping
- [Rust resume holds a declared end](/quest/m1/resume-rust-end.md) - a resumed Rust track keeps a declared end across copies, dedupes flapping copies, and judges retention inside the reader's window
- [JS answers SUBSCRIBE_START like Rust](/quest/m1/js-start-parity.md) - a JS publisher's START names where its backlog starts, not its newest group
- [Received timestamps are media time](/quest/m1/lite-time-provenance.md) - a received timed track's timestamps are media time, so the rewind check can run across hops
Expand Down
2 changes: 1 addition & 1 deletion quest/m1/audit-compile-time.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ selected 29 Rust packages (moq-net and every dependent, including C, FFI,
GStreamer and WASM). Phases: rustdoc 1m41s (678 hits, 76 misses, 34 bypassed),
WASM clippy 18.8 s, io-uring clippy 33.5 s, test build 5m40s (524 hits, 83
misses, 5 bypassed). Nextest started 7,890 tests and stopped after 663 on the
known [failover flake](/quest/m1/test-flakes-2/redundant-failover-drift.md).
known [failover flake](/quest/m0/redundant-failover-drift.md).
Cargo rebuilds every dependent when an upstream crate's source changes at all,
even a comment, so the test build is the bulk of the compile cost.

Expand Down
2 changes: 1 addition & 1 deletion quest/m1/audit-test-cost.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ the stats session graph's recursive Send proof (send-depth and fuzz),
bench/ffi-gen's stale wasm-bindgen lock, moq-uring's fixed 1 ms calibration
slack, the Nightly JS benchmark stall, the CI selection test's feature count,
and the stats producer benchmark lint. It planned
[redundant failover drift](/quest/m1/test-flakes-2/redundant-failover-drift.md)
[redundant failover drift](/quest/m0/redundant-failover-drift.md)
and [macOS QUIC connects](https://github.com/moq-dev/moq/pull/6110).

Baselines from queue run
Expand Down
4 changes: 4 additions & 0 deletions quest/m1/close-tail-drain.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ moq-tokio's `quic::web` path) for the same ordering.

Public API: none. Wire: none.

## Required

- [qmux on the QUIC stream state machine](/quest/m1/quic-qmux.md) - an unfinished qmux read ends after its buffered data once the session closes, which the drain relies on over qmux

## Related

- [QUIC ack hook](/quest/m2/quic-ack-hook.md) - a FIN/ack hook in the QUIC layer
1 change: 1 addition & 0 deletions quest/m1/compress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ This README owns the end-to-end proof and the guide once every child lands:

## Required

- [Mid-group subscriptions widen](/quest/m1/compress/app-range-widen.md) - an application range starting mid-group on a compressed track widens to frame 0 at the subscriber instead of failing with `Lagged`
- [IETF extension](/quest/m1/compress/ietf.md) - moq-transport draft-16+ peers that negotiate the extension exchange compressed tracks
- [JavaScript](/quest/m1/compress/js.md) - js/net publishes and consumes compressed tracks on lite-07
- [Migrate](/quest/m1/compress/migrate.md) - catalogs, stats, and timelines publish one compressed track; moq-flate, @moq/flate, and every `.z` name are gone
Expand Down
31 changes: 31 additions & 0 deletions quest/m1/compress/app-range-widen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# [S] A mid-group subscription to a compressed track widens to frame 0

## Goal

An application subscription whose range starts mid-group on a compressed
track succeeds: the subscriber's requesting model widens the request to frame
0 of that group before it leaves, and the reader gets the frames it asked for.
Today it fails with `Lagged`. The server never widens and keeps serving
exactly what is asked.

## Plan

Decided with the maintainer on 2026-10-20, from
[#5928](https://github.com/moq-dev/moq/pull/5928)'s Decision 6, which the
[epic](/quest/m1/compress/README.md)'s Ranges rule records: the subscriber
widens, never the server.

- Route-change resume already widens to frame 0. This covers ranges an
application supplies, through the same requesting-model path rather than a
second rule.
- Widen only when the subscriber lacks the group's prefix; one that holds
frames 0..N-1 still asks from N.
- The reader still starts at the frame it asked for: the widened prefix only
feeds the inflater.

Regression test on mock time, in Rust: a mid-group application subscribe on a
compressed track returns the requested frames instead of `Lagged`, and the
wire request starts at frame 0. Mirror it in `js/net` if
[JavaScript](/quest/m1/compress/js.md) has landed compressed tracks by then.

Public API: none. Wire: none.
1 change: 1 addition & 0 deletions quest/m1/drop-set.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,5 +219,6 @@ unchanged.

## Related

- [An evicted group is judged stale](/quest/m1/evicted-group-stale.md) - the sibling for groups a reader already holds after the cache evicts them
- [Subscribe ranges](/quest/m1/subscribe-ranges/README.md) - fills the cache miss an evicted run leaves
- [JS unbounded groups](/quest/m2/unbounded-groups/js.md) - the JS byte bound for groups and the runs between them; not required, since JS runs never outgrow the JS cache
50 changes: 50 additions & 0 deletions quest/m1/evicted-group-stale.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# [S] An evicted group is judged stale by its successor

## Goal

A group already handed to a reader stays judgeable after the cache evicts
it: once its successor's arrival plus the reader's budget has passed, the
group is stale, the same verdict `hooks.staleAt` already gives a resumed
copy's group by sequence alone. A serve loop that waits on such a group (a
group stream whose FIN never completes) is then reset as `Old`, whatever the
budget.

Today the verdict needs the group to still be cached. Rust `GroupExpiry`
(`rs/moq-net/src/model/track.rs`) requires the sequence in the cache lookup,
and JS `#isStale` (`js/net/src/track.ts`) requires the group in the
timeline. Both caches evict an idle group after their window (Rust
`cache::DEFAULT_EXPIRY`, JS `CACHE_WINDOW`, both 30 s), so under a budget of
about 30 s or more a stalled group is never convicted. Since
[#6059](https://github.com/moq-dev/moq/pull/6059) the same stalled stream
also holds the IETF PUBLISH_DONE, which waits on a finished group's FIN
under the budget.

## Plan

Found in #6059's follow-ups and decided with the maintainer on 2026-10-20 as
a sibling of [Drop set](/quest/m1/drop-set.md): that quest judges missing
groups, this one judges groups a reader already holds.

- Judge by the successor, as `#staleAt` does for a sequence the track does
not hold. The verdict needs the successor's arrival (and its reach, for the
media-time arm), not the evicted group itself.
- Find out what the cache-membership checks guard before dropping them, such
as a different copy now occupying the sequence, and keep that guard.
- Settle what the verdict reads once the successor is evicted too. It must
never convict early: any later group arrived no earlier than the successor,
and the newest group is never stale.
- Check `FetchExpiry` beside `GroupExpiry`, which has the same cache-lookup
condition.

Regression tests on mock time, Rust and JS, each failing without the fix,
with a budget of at least the cache window: a handed-out group whose stream
never completes is evicted, then reset as `Old` once its successor's arrival
plus the budget passes, and on IETF the PUBLISH_DONE then goes out. The
newest group is still never convicted.

Public API: none. Wire: none.

## Related

- [Drop set](/quest/m1/drop-set.md) - each cursor judges missing groups against its own budget
- [#6059](https://github.com/moq-dev/moq/pull/6059) - bounds a stalled group stream's FIN wait by the age budget, which this extends past eviction
46 changes: 46 additions & 0 deletions quest/m1/ietf-setup-validity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# [XS] Every IETF setup wait runs under request validity

## Goal

Every wait while the IETF subscriber sets up a request ends as soon as the
request stops being valid (its route retired, or its grant revoked, narrowed,
or expired), with the same local refusal and peer cancellation that the
SUBSCRIBE's request-ID and SUBSCRIBE_OK waits give since
[#6061](https://github.com/moq-dev/moq/pull/6061). In
`rs/moq-net/src/ietf/subscriber.rs` that covers:

- `Stream::open` for SUBSCRIBE, TRACK_STATUS, the cache-miss group FETCH,
and the joining FETCH on draft 17+;
- the request-ID wait for a fetch-only TRACK_STATUS;
- the cache-miss group FETCH's request-ID and response waits, whose response
wait still uses a `retired` flag.

Today each is bounded only by QUIC stream credit or the ten-second
request-ID give-up.

## Plan

Decided with the maintainer on 2026-10-20, from #6061's follow-ups:

- Reuse #6061's poll-native shape: poll the gate and `validity` beside
`Control::poll_credit(waiter, &mut Deadline)` or the response read inside
one wait, rather than racing the whole setup future. An allocated ID is
still sent on drafts 14-16, since the peer grants MAX_REQUEST_ID credit
back only for requests it receives; the response wait then cancels it.
- Refuse the request locally before awaiting the peer's acknowledgement of
the cancellation, as #6061 does.
- If [Rust request credit](/quest/m1/rs-request-credit.md) lands first,
`Stream::open` no longer waits for stream credit, and only the request-ID
and response waits remain here.

Regression tests on sim time, one per wait, each failing on main by timing
out: a revoked, narrowed, and expired grant and a route retirement during
the wait each refuse the request promptly, on drafts 14 and 16 and on 17+
where the wait differs.

Public API: none. Wire: none.

## Related

- [#6061](https://github.com/moq-dev/moq/pull/6061) - brings the SUBSCRIBE's own request-ID and SUBSCRIBE_OK waits under the grant
- [Rust request credit](/quest/m1/rs-request-credit.md) - a request without stream credit fails at once instead of waiting in `Stream::open`
Loading
Loading