Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions quest/m1/ffi-shape/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ are renamed.
Kotlin, Dart, and Python, whatever the generated name. Swift keeps its
generated `<Enum>.<variant>` 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
Expand Down
24 changes: 14 additions & 10 deletions quest/m1/hls-bounded.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions quest/m1/js-broadcast-demand.md
Original file line number Diff line number Diff line change
@@ -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.
42 changes: 42 additions & 0 deletions quest/m1/merge-queue-settings.md
Original file line number Diff line number Diff line change
@@ -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
15 changes: 11 additions & 4 deletions quest/m1/origin-front-parks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
7 changes: 7 additions & 0 deletions quest/m1/quest-flat-lines.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
25 changes: 25 additions & 0 deletions quest/m1/transcode-fetch-demand.md
Original file line number Diff line number Diff line change
@@ -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()`
Loading