diff --git a/quest/m1/README.md b/quest/m1/README.md index 0a44964597..dbf0d0bd88 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -40,8 +40,11 @@ blocks. The quests that gated m0 lines moved under them. - [Cluster routing](/quest/m1/cluster-routing/README.md) - any node routes toward a broadcast's origin over CDN and P2P links alike, with per-origin routes and path-less announces - [One route cost](/quest/m1/route-cost.md) - Warm and Cold collapse to one static route cost - [Track tail interop](/quest/m1/track-tail-interop.md) - a Rust publisher ending a track with a group in flight is read to its end by the JS subscriber, and the reverse, in `just test interop` +- [Transcode fetch demand](/quest/m1/transcode-fetch-demand.md) - the transcode fetch handler drops a request once nobody wants the group +- [JS broadcast demand](/quest/m1/js-broadcast-demand.md) - `@moq/net`'s `Broadcast.Demand` matches Rust in three edge cases - [Flat questlines](/quest/m1/quest-flat-lines.md) - moq pins the current quest CLI, lands its questline branches on main, and retires them - [moq-bot may push workflow changes](/quest/m1/bot-workflows-permission.md) - condition: the maintainer grants moq-bot's GitHub App the `workflows` permission, so back-merges carrying workflow changes go through +- [main merges through a squash queue](/quest/m1/merge-queue-settings.md) - condition: once `release` carries the new back-merge script, the maintainer enables the squash merge queue and moq-bot's pull_request bypass together - [Binding audio delay](/quest/m1/binding-surface.md) - moq-ffi and every wrapper configure and observe audio playout delay - [moq-net owns its transport seam](/quest/m1/transport-seam.md) - moq-net names only its own transport traits, so a web-transport-trait or qmux major bump is a patch for it - [fMP4 init from the catalog](/quest/m1/fmp4-catalog-init.md) - fMP4 export writes avc3/hev1 entries from the catalog, so an Annex-B H.264 or H.265 init no longer waits for the first keyframe diff --git a/quest/m1/ffi-shape/net.md b/quest/m1/ffi-shape/net.md index f057703119..350c7e6196 100644 --- a/quest/m1/ffi-shape/net.md +++ b/quest/m1/ffi-shape/net.md @@ -30,6 +30,11 @@ are renamed. Kotlin, Dart, and Python, whatever the generated name. Swift keeps its generated `.` cases, since it cannot alias a case. - `TrackProducer` drops `name`/`is_used`/`used`/`unused` for `demand()`. +- `GroupRequest` gains `demand()` too, in moq-ffi, moq-c, and every wrapper, + so a binding's fetch handler can tell when every caller left and drop the + request (decided 2026-10-05, from #4708, which moved Rust's + `group::Request` onto `demand()` and withdraws an abandoned fetch on the + last caller's drop). - The renames no additive change could make: - `subscribe` to `consume` on Python `Client`/`connect`, Kotlin `Moq.connect`/`Server.listen`, Dart `ConnectOptions`/`ListenOptions`, and Go diff --git a/quest/m1/hls-bounded.md b/quest/m1/hls-bounded.md index bf3bbb187b..81a4ece713 100644 --- a/quest/m1/hls-bounded.md +++ b/quest/m1/hls-bounded.md @@ -25,13 +25,13 @@ wiring. Rejected: keeping them in moq.pro. Also decided 2026-10-05: this quest lives at the m1 root on `main`, outside the archive line, since live timelines need it first. -The offline archive HLS that `quest/m1/archive/hls.md` used to describe is -done on the archive line's branch (#4115, #4155, #4169). The per-track gap -behaviour lives on that line's -[Per-track timelines](/quest/m1/archive/track-timeline.md) (#4280, `spans.rs`), which -derives segments at the edge from group timestamps and emits `EXT-X-GAP` for -a rendition with no group start in a span. Whichever lands second rebases -onto the other, and the rules here hold for both shapes. +The offline archive HLS that `quest/m1/archive/hls.md` used to describe +landed on `main` with the archive line (#4034: #4115, #4155, #4169). So did +the per-track gap behaviour from +[Per-track timelines](/quest/m1/archive/track-timeline.md) (#4280, `spans.rs`), +which derives segments at the edge from group timestamps and emits +`EXT-X-GAP` for a rendition with no group start in a span. The rules here +apply to that shape. Start by auditing `rs/moq-hls/src/export` against each rule; much exists (`segments.rs` numbers aligned segments and carries gap rows, `playlist.rs` @@ -48,9 +48,13 @@ writes `EXT-X-GAP`). Fix only what a test shows missing: - **Capped window, decided 2026-10-05.** A durable (store-backed) timeline still advertises a capped sliding window: a live playlist lists a bounded window even when the store retains more. A full VOD or EVENT listing is a - separate, explicit replay mode. This conflicts with #4155's durable listing - on the archive line branch, which lists the whole durable timeline without - the live window; reconcile it there when that branch next merges `main`. + separate, explicit replay mode. This reverses #4155, now on `main`: a + durable timeline (`durable()` in `export/mod.rs`) lists everything it + retains, past `Config::window`, and + `a_durable_timeline_lists_past_the_window` pins it. This quest owns the + reconciliation (decided 2026-10-05): the durable listing becomes this + capped window, the full listing becomes the explicit replay mode, and that + test and the `Config::window` docs change with it. - **Stable sequence.** `EXT-X-MEDIA-SEQUENCE` is the window's first segment number, derived from the timeline, so two edges and a reload after a pop agree. diff --git a/quest/m1/js-broadcast-demand.md b/quest/m1/js-broadcast-demand.md new file mode 100644 index 0000000000..d4b8e18a75 --- /dev/null +++ b/quest/m1/js-broadcast-demand.md @@ -0,0 +1,20 @@ +# [XS] JS broadcast demand matches Rust + +## Goal + +`@moq/net`'s `Broadcast.Demand` (`js/net/src/broadcast.ts`) agrees with Rust's +broadcast demand in the three cases where it differs today, each with a test: + +- A pending `resolveTrackInfo` query counts as demand, as a pending track + request does in Rust. +- `removeTrack` stops counting the removed track at once, instead of until it + closes. +- Inserting an already-closed track leaves no cleanup entry behind. + +## Plan + +Found while landing #4708, which introduced `Broadcast.Demand` and per-level +`demand()` handles in both languages. Nothing outside the bench reads +`Broadcast.Demand` yet, so this is parity, not a live bug. + +Public API: none beyond the corrected behavior. Wire: none. diff --git a/quest/m1/merge-queue-settings.md b/quest/m1/merge-queue-settings.md new file mode 100644 index 0000000000..6416b95441 --- /dev/null +++ b/quest/m1/merge-queue-settings.md @@ -0,0 +1,42 @@ +# [XS] main merges through a squash queue + +## Goal + +Condition: once `release` carries #4619's `sh/gh/back-merge.sh`, the +maintainer switches `main` to the merge queue in ruleset 2420853, making both +changes at once: + +- Enable "Require merge queue" with method Squash, keeping Check and Test as + required checks. +- Add moq-bot as a bypass actor with mode `pull_request`, so the `land` job in + `.github/workflows/check.yml` can merge the release back-merge as a merge + commit outside the queue. + +Order matters. Until `release` carries the new script, a push to `release` +runs the old copy, which enables auto-merge on the back-merge PR; under a +squash queue that squashes it. With the queue on but no bypass, `land`'s merge +is refused. + +Check: `git show origin/release:sh/gh/back-merge.sh` no longer runs +`gh pr merge --auto`, and +`gh api repos/moq-dev/moq/rulesets/2420853` lists a `merge_queue` rule with +merge method `SQUASH` and a moq-bot bypass actor with `bypass_mode` +`pull_request`. Delete this quest once both hold and the Dependabot check +below is done. + +## Plan + +Decided in #4619 (2026-10-05): a squash queue with a pull_request-only bot +bypass for the back-merge, landed by the `land` job; the back-merge may land +against a `main` that moved during its CI run, accepted. + +Check once both settings are on: `.github/workflows/dependabot.yml` merges +with the same moq-bot app token through `gh pr merge --auto --squash`, without +`--admin`, so its merges may still enqueue rather than bypass. On the next +Dependabot PR, see whether it went through the queue or skipped it as a +bypass actor, and record the result in the PR that deletes this quest. If it +skipped the queue, ask the maintainer whether that is acceptable. + +## Related + +- [moq-bot may push workflow changes](/quest/m1/bot-workflows-permission.md) - the other moq-bot permission the back-merge needs diff --git a/quest/m1/origin-front-parks.md b/quest/m1/origin-front-parks.md index f6396c289c..57161e3e31 100644 --- a/quest/m1/origin-front-parks.md +++ b/quest/m1/origin-front-parks.md @@ -25,6 +25,17 @@ benchmark swept over requesters and route-table churn would show whether the re-mint is a real slope or noise, and it is the same benchmark that would show the replacement is cheaper. +Also reclaim the filtered front a peer session leaves behind (folded in +2026-10-05). #4922 keyed fronts by `Horizon::effective`, so viewers share the +plain front, but a hop that appears in a covering route chain still gets its +own filtered front, and that front lives as long as the route. A peer that +both publishes and subscribes, or reconnects with a fresh hop, leaves one +behind per peer session. #4922 scoped it out because it scales with peer +sessions, not viewers. Test that the front count returns to the plain front +after the peer session closes. If this quest moves to m2, split this leftover +into its own m1 quest: it is a leak, not a question of whether the retry loop +costs anything. + Public API: no signature change expected. `routed_broadcast` and `request_broadcast` keep their contracts; only where the waiting happens changes. @@ -33,7 +44,3 @@ Decided in the 2026-09-30 audit: this may move to m2 if that benchmark shows the retry loop's re-mint is noise. Fronts are now keyed by effective exclusion (`Horizon::effective`), so viewers share them; the `origin/viewer_*` benches do not cover the retry loop. - -## Required - -- [Wildcard](/quest/m0/wildcard/README.md) - `routed_broadcast`, `request_broadcast`, and the pool code this reworks exist only on its line branch diff --git a/quest/m1/quest-flat-lines.md b/quest/m1/quest-flat-lines.md index 6a0eb5ff1b..c7d4c3a71d 100644 --- a/quest/m1/quest-flat-lines.md +++ b/quest/m1/quest-flat-lines.md @@ -73,6 +73,13 @@ Remaining: with the branch: #4675 (auth/request-token) needs the updated auth line merged in. #4645 (tstd/delay) now targets `main`; #4732 and #4863 landed. +- Retiring the auth branch reconciles `quest/m1/auth/` with `main`'s copy + (decided 2026-10-05). Drop the children the branch finished or moved (lite, + interop, unauthorized, auth-ok-preflight, error-codes, narrowing, peer-grant, + moq-transport), keep the branch's new ones (wip-version, not-supported, + violations, js-fetch-watch), and fold violations' lite decode gap into + [malformed grant](/quest/m1/auth/malformed-grant.md). The branch's + wip-version quest moves its lite-06 wire text to `moq-lite-07-wip`. Done when `flake.nix` pins the new quest, no `quest/*README` branch remains, and `quest check` passes. diff --git a/quest/m1/transcode-fetch-demand.md b/quest/m1/transcode-fetch-demand.md new file mode 100644 index 0000000000..1d79610c83 --- /dev/null +++ b/quest/m1/transcode-fetch-demand.md @@ -0,0 +1,25 @@ +# [XS] A transcode fetch stops when nobody wants the group + +## Goal + +moq-transcode's fetch handler (`fetch()` in `rs/moq-transcode/src/rung.rs`, +reached from `requested_group`) watches `request.demand()` and drops the +request once it goes unused, so a group every caller abandoned is never +encoded. Test: an abandoned fetch encodes nothing, and a late retry after the +last caller left gets a fresh request rather than racing a stale encode. + +## Plan + +Found while landing #4708, which moved `group::Request` onto `demand()` and +withdraws an abandoned fetch when its last caller drops. The transcode handler +never watches demand, so a late retry still encodes the group and its +`accept` logs `Duplicate`. Wasted work, not a correctness bug. + +Build on #4812, which landed first and made the same `fetch()` refuse a +mid-group start before it fetches the source. + +Public API: none. Wire: none. + +## Related + +- [Transcoders start at group boundaries](/quest/m0/wildcard/transcode-group-start.md) - #4812 changed the same `fetch()`