diff --git a/quest/m0/README.md b/quest/m0/README.md index 11842d46a1..8f7672474e 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -60,6 +60,7 @@ remain and no release waits on them. - [Self-hosted CI](/quest/m0/self-hosted-ci.md) - same-repo Check and Test run on a self-hosted NixOS runner with a main-written local cache, behind a `CI_RUNNER` kill switch - [CI host](/quest/m0/ci-host.md) - the maintainer brings up the spare desktop as the `moq-ci` and `moq-gpu` runner host - [Draft-22 media on 0.17](/quest/m0/release-22/README.md) - a 0.17.x with the LOCATION_FILTER and FIRST_OBJECT fixes and moq-noq 1.3.5, before Seattle +- [TS duration fidelity](/quest/m0/ts-duration-fidelity.md) - Interop's TS compliance captures the whole round-tripped stream again, fixing a regression that turns Interop red on main - [Paused spinner](/quest/m0/watch-paused-spinner.md) - the watch buffering spinner never covers the paused play button, so Interop's resume step can click it - [Check base](/quest/m0/check-base.md) - `just check` diffs against the PR's base whatever the local branch tracks, so scoped checks stay scoped - [web-transport releases the qmux fixes](/quest/m0/qmux-credit-upstream.md) - waiting on moq-dev/web-transport#412 and #413 to merge and ship, which qmux credit bumps to diff --git a/quest/m0/ts-duration-fidelity.md b/quest/m0/ts-duration-fidelity.md new file mode 100644 index 0000000000..c4288eec9c --- /dev/null +++ b/quest/m0/ts-duration-fidelity.md @@ -0,0 +1,29 @@ +# [S] TS compliance duration-fidelity captures the whole source + +## Goal + +Interop's TS-compliance `duration-fidelity` check (`test/ts/run.sh`) captures +the whole round-tripped stream, not a fraction of it, fixed at its cause. + +## Plan + +Moved to m0 on 2026-10-10: it now fails on every Interop run, main +included (`c0c0bb33`, 3.2 s of 20.1 s, and each head of #5099), so it reads +as a regression, not a flake. A red Interop on main hides real regressions +in every PR. Start by finding the last green Interop run on main and the +first red one, and bisect between them. #5217's isolated local run captured +the whole source (20.47 s of 20.06 s), so look at what differs on the CI +runner. + +First seen 2026-10-10 on #5158's second Interop run: the export captured 3.3 s of a +20.1 s source, and the check failed; it also failed once on #5140 and passed +on rerun. Find whether the export ends early (an announce `End`, linger, or +a catalog finish arriving before the media), starts late, or the capture is +cut off by the harness. Fix that at its source, with a regression that fails +without it. No longer timeout and no lower duration threshold. + +#5147 (export ts follows announcements, merged 2026-10-10) changed how an +export ends, so check it first. Never raise a timeout or add a retry (the +test-flakes-2 rules still apply). + +Public API: none expected. Wire: none expected. diff --git a/quest/m1/README.md b/quest/m1/README.md index 8498025f09..7fa7b75bbe 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -81,6 +81,7 @@ bench all require its crate. - [moq.sh deploys from CI](/quest/m1/moq-sh-deploy.md) - the first `release` run of the moq.sh workflow deploys with the Workers Editor token - [Plan: untimed verbatim PES](/quest/m1/plan-ts-pes-untimed.md) - decide how a verbatim TS track carries a PES that has no PTS, then write the implementation quest - [Data consumer timestamps](/quest/m1/data-consumer-timestamps.md) - json and binary consumers return each value's timestamp, in Rust and every binding; snapshots add `latest()` beside an in-order `next()` +- [JSON window timestamps](/quest/m1/json-window-timestamps.md) - Rust and JS window consumers return each event's frame timestamp like the snapshot and stream consumers - [IETF object gaps](/quest/m1/ietf-object-gaps.md) - a gapped object ID is refused loudly like a subgroup in Rust and JS, and an overflowing one closes the session in Rust - [JavaScript FETCH](/quest/m1/js-fetch.md) - browser publishers answer IETF FETCH through the JS ranges request surface - [Relay session limits](/quest/m1/relay-session-limits.md) - moq-relay sets per-session request limits, tighter for clients than peers, and the bindings name a refused request @@ -198,6 +199,7 @@ bench all require its crate. - [Draft 14-16 updates](/quest/m1/ietf-legacy-updates.md) - Rust and JS apply and answer moq-transport 14-16 request updates without ending or leaking the request - [JS session caps](/quest/m1/js-session-parity.md) - @moq/net enforces moq-net's per-session announce and subscription caps - [Tail arrivals](/quest/m1/tail-arrivals.md) - a track judges its pending tail from recorded arrivals, not a cache scan, and errors a reader whose tail ends short +- [Resume reorder](/quest/m1/resume-reorder.md) - a group whose stream header arrives after its successor's still resumes on failover; only a dropped or long-gone group fails - [JS pending tail](/quest/m1/js-pending-tail.md) - a JS reader holds for a track's pending tail like Rust - [Group demand after accept](/quest/m1/group-demand-accept.md) - an accepted group request's demand ends cleanly instead of failing with NotFound - [Pool churn cursors](/quest/m1/pool-churn-cursors.md) - the origin pool churn benchmark sweeps announce cursors per prefix diff --git a/quest/m1/json-window-timestamps.md b/quest/m1/json-window-timestamps.md new file mode 100644 index 0000000000..b3de92da6a --- /dev/null +++ b/quest/m1/json-window-timestamps.md @@ -0,0 +1,32 @@ +# [S] JSON window consumers return each event's timestamp + +## Goal + +The moq-json and `@moq/json` window consumers return each event with a +timestamp. In JS all three data consumers then return `Timed`; the Rust +snapshot and stream consumers follow in +[Data consumer timestamps](/quest/m1/data-consumer-timestamps.md). + +## Plan + +Follow-up from #5099, which gave `@moq/json` and `@moq/flate` snapshot and +stream consumers `Timed`. The window consumer (`window::Consumer::next` in +`rs/moq-json` and `js/json`) still yields a bare `Event`. + +- Decided (2026-10-10): `next()` returns `Timed>`. `at` is the + timestamp of the frame that carried the event, and `None` when untimed. A + record restated by a group header gets the header frame's time; document that + on the type. There is no per-record time on the wire, so no wire change. +- Decided: Rust and JS together, same names and shape. Update moq-ffi and the + bindings if they expose the window consumer (see the Cross-Package Sync table). +- Update in-repo readers (`js/hang` timeline, `js/room` chat) and add a line to + the Unreleased section of `doc/setup/upgrade.md`. No new doc page. +- Land before the next `@moq/json` release, so its break ships together with + #5099's. Not blocked on the Rust snapshot and stream quest: both Rust breaks + use the same `moq_net::Timed` and ship in the next moq-json release together. + +Public API: breaking (Rust and JS). Wire: none. + +## Related + +- [Data consumer timestamps](/quest/m1/data-consumer-timestamps.md) - the Rust snapshot and stream side of the same change diff --git a/quest/m1/resume-reorder.md b/quest/m1/resume-reorder.md new file mode 100644 index 0000000000..7f54330663 --- /dev/null +++ b/quest/m1/resume-reorder.md @@ -0,0 +1,33 @@ +# [M] A track judges a passed group from its arrivals, not its newest sequence + +## Goal + +When a route fails over, a group whose stream header arrives after its +successor's still continues on the new route. Only a group the publisher +dropped, or one whose gap outlived its grace, ends with an error. Held fetches +over a pre-lite-05 upstream (#5209) and route resume use the same rule. + +## Plan + +Found by Codex on #5209 (2026-10-10). `track::Consumer::poll_group(seq)` +answers that the feed went past `seq` as soon as any newer group arrived +(`max_sequence > seq`). Each group is its own QUIC stream, so N+1's header can +beat N's. Resume (`rs/moq-net/src/model/resume.rs`, the refusal verdict) then +fails an in-flight group it could have continued. + +- Decided: build on [Tail arrivals](/quest/m1/tail-arrivals.md), the per-track + record of live arrivals in the model, and make `poll_group`'s "passed" verdict + read it. A gap stays open for the subscription's grace, as `tail::Tail` does + in the lite subscriber today. +- Decided: #5209's held fetches move off their local `Tail` read onto this + verdict, so one rule remains. +- The delivery budget alone was rejected: relay tracks from lite-01..04 + upstreams are untimed, so nothing on them is ever stale. +- Tests use mocked time: a failover mid-group where N+1's header arrives before + N's continues N, and a truly dropped group still fails once its gap folds. + +Public API: behavior only. Wire: none. + +## Required + +- [Tail arrivals](/quest/m1/tail-arrivals.md) - the per-track arrival record this verdict reads diff --git a/quest/m1/test-flakes-2/README.md b/quest/m1/test-flakes-2/README.md index fb20351213..8e3816da9e 100644 --- a/quest/m1/test-flakes-2/README.md +++ b/quest/m1/test-flakes-2/README.md @@ -33,7 +33,6 @@ Public API: none. Wire: none. ## Required - [Interop close code](/quest/m1/test-flakes-2/interop-close-code.md) - the browser close-code test reliably sees `unauthorized` for a refused session -- [TS duration fidelity](/quest/m1/test-flakes-2/ts-duration-fidelity.md) - TS compliance captures the whole round-tripped stream - [Cluster burst overrun](/quest/m1/test-flakes-2/cluster-burst-overrun.md) - the impaired cluster burst drill always overruns its bottleneck, so it always exercises it - [TS passthrough jump](/quest/m1/test-flakes-2/ts-passthrough-jump.md) - moq-cli's flagged-jump TS passthrough test passes under load without wall-clock pacing diff --git a/quest/m1/test-flakes-2/ts-duration-fidelity.md b/quest/m1/test-flakes-2/ts-duration-fidelity.md deleted file mode 100644 index ac4c232c95..0000000000 --- a/quest/m1/test-flakes-2/ts-duration-fidelity.md +++ /dev/null @@ -1,20 +0,0 @@ -# [S] TS compliance duration-fidelity captures the whole source - -## Goal - -Interop's TS-compliance `duration-fidelity` check (`test/ts/run.sh`) captures -the whole round-tripped stream, not a fraction of it, fixed at its cause. - -## Plan - -Seen 2026-10-10 on #5158's second Interop run: the export captured 3.3 s of a -20.1 s source, and the check failed; it also failed once on #5140 and passed -on rerun. Planned under this questline's cause-first rules (decided -2026-10-10): find whether the export ends early (an announce `End`, linger, or -a catalog finish arriving before the media), starts late, or the capture is -cut off by the harness. Fix that at its source, with a regression that fails -without it. No longer timeout and no lower duration threshold. - -Check against #5147 (export ts follows announcements), which may change how an export ends. - -Public API: none expected. Wire: none expected.