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
2 changes: 2 additions & 0 deletions quest/m1/explicit-state/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ which collapses that state and waits on [Drop set](/quest/m1/drop-set.md).
- [Track and group end-of-life](/quest/m1/explicit-state/track-end.md) - moq-net's track and group outcomes are one enum each, in Rust and JS
- [moq-net phase data](/quest/m1/explicit-state/net-phases.md) - moq-net's session and subscription state machines keep each phase's data in its variant
- [moq-mux and moq-gst lifecycles](/quest/m1/explicit-state/mux-gst.md) - export sources, TS and fMP4 export tracks, and gst pads are explicit state
- [A failed catalog finish is final](/quest/m1/explicit-state/catalog-finish.md) - the catalog producer never reports closed with a catalog track left open
- [Video's declared-end freeze is not a stall](/quest/m1/explicit-state/video-declared-end.md) - video holding its last picture through a declared end counts no stall, like audio's declared silence

## Related

Expand Down
24 changes: 24 additions & 0 deletions quest/m1/explicit-state/catalog-finish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# [XS] A failed catalog finish is final

## Goal

`moq_mux::catalog::Producer::finish` is never half-finished: when finishing
either catalog track fails, the producer reports that failure for good and
both catalog tracks end. It is never left closed with a track still open.

## Plan

Planned 2026-11-07 (maintainer, /quest-complete), a follow-up of
[moq-dev/moq#8210](https://github.com/moq-dev/moq/pull/8210), which fixed
this shape in the importers and the container, audio, and JSON producers
but left the catalog producer outside its list.

`finish` (`rs/moq-mux/src/catalog/producer.rs`) stores
`closed = Some(Error::Closed)` before `self.outputs.hang.finish()?` and
`self.outputs.msf_track.finish()?`. If the hang track's finish fails, the
MSF track is never finished, yet every later call reports a clean `Closed`.
Record the terminal outcome after the fallible work, as #8210 did: keep the
first error, abort or end the other track, and store the failure rather
than `Closed`. Test a failing track finish.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

Public API: none. Wire: none.
20 changes: 20 additions & 0 deletions quest/m1/explicit-state/video-declared-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# [XS] Video's declared-end freeze is not a stall

## Goal

`<moq-watch>` doesn't count a stall while video holds its last picture
through a publisher's declared end, as audio already doesn't for a declared
silence. The stall count, stalled time, and the echo `stalls`/`stalled`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
feedback agree between audio and video.

## Plan

Planned 2026-11-07 (maintainer, /quest-complete), a follow-up of
[moq-dev/moq#8209](https://github.com/moq-dev/moq/pull/8209), which made
watch count a stall only while a rendition plays. It left this one case as
on `main`: a picture held through a declared end for longer than the 500 ms
buffering timer still counts as a stall. Give video's playback state a
declared-end phase, as audio's handles a declared silence, so the buffering
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
timer doesn't run there. Test a declared end held past 500 ms.

Public API: none. Wire: none.
1 change: 1 addition & 0 deletions quest/m2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ quests that require them.
- [A recreated or restarted track cannot reuse group sequences](/quest/m2/sequence-reuse.md) - recreating a track or restarting an instance cannot give new content an old group identity unless the app seeds past it, and datagrams are documented as exempt
- [Subscriptions name the oldest group without a u64::MAX sentinel](/quest/m2/anchor-oldest.md) - `Anchor` gets an explicit oldest variant, decoded the same way on every wire
- [moq auth serve bounds what a leaked token or public caller can do](/quest/m2/auth-serve-policy.md) - tokens need an `exp` unless the operator opts out, and a public listener authenticates relays
- [Finishing a producer returns a Finished handle](/quest/m2/finished-handle.md) - `finish(self)` consumes moq-net's and the media crates' producers, so appending after finish doesn't compile
- [Cut readiness](/quest/m2/cut-gate/README.md) - the checks the cut needs that break nothing: main no slower than release, vendored fixes ported, and moq-c at parity
- [Probe](/quest/m2/probe/README.md) - a session probes up to the bitrate it wants to send or receive, so a stopped simulcast rendition resumes and a viewer up-switches; browser padding is parked in m3 (held in m2 by open claims; moves to m3)
- [Finish the milestone re-cut](/quest/m2/recut-held.md) - claim-held epics move to the milestone their kind and priority give them once their claims merge
48 changes: 48 additions & 0 deletions quest/m2/finished-handle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# [L] Finishing a producer returns a Finished handle

## Goal

`finish(self)` consumes a producer and returns a `Finished` handle, so
appending after finish is a compile error instead of a runtime one. The
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
handle keeps what finishing still allows: aborting, which reports a later
failure and releases the cache, and, where the type supports it today,
inserting below the end, such as a late group under a finished track's end.
It has no append. One shape covers moq-net's `track::Producer` and
`group::Producer` and the media producers and importers, with JS mirrors.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

## Plan

Decided 2026-11-07 (maintainer, /quest-complete), a follow-up of
[moq-dev/moq#8210](https://github.com/moq-dev/moq/pull/8210), which made a
failed finish final with explicit state and recommended against
`finish(self)`. The maintainer chose the typestate:

- ✅ **Scope: moq-net and media.** moq-net's `track::Producer` and
`group::Producer`, plus #8210's producers and importers (the container
producer, the MKV, fMP4, FLV, and TS importers, the audio and JSON
producers), and their `js/net` and `js/hang` mirrors. Not chosen: moq-net
Comment thread
kixelated marked this conversation as resolved.
Resolved
only; media only.
- ✅ **Name: `Finished`**, as `track::Finished`, `group::Finished`, and so
on.

Today each producer borrows on finish and enforces the rest at runtime.
`track::Producer::finish(&self)` (`rs/moq-net/src/model/track.rs`) sets the
end after `max_sequence`, so appending at or above it fails, while older
groups below it can still arrive. `group::Producer::finish(&mut self)`
(`rs/moq-net/src/model/group.rs`) borrows so that `abort` can still report
a later failure and its cache stays readable. `Finished` makes exactly that
contract the type. Holding a finished importer or producer, which #8210
documents as a use, becomes holding its `Finished`. Decide per type what
`Finished` exposes beyond abort, such as read access to the cache or insert
below the end, without adding anything the producer can't do today.

This breaks every caller that finishes and then keeps using the producer.
List them in the PR, with the migration for each package in its Impact section.

Public API: breaking in moq-net, moq-mux, moq-audio, moq-json, and their JS
packages. Wire: none.

## Related

- [Track and group end-of-life](/quest/m1/explicit-state/track-end.md) - the outcome enums `Finished` would wrap
- [A failed finish is final](https://github.com/moq-dev/moq/pull/8210) - the explicit state this builds on
Loading