Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e841f47
chore(quest): claim quest/m1/compress/ietf-range-widen.md
kixelated Oct 23, 2026
fdd55e5
fix(net): widen moq-transport mid-group starts on compressed tracks
kixelated Oct 23, 2026
cce4513
fix(net): start every upstream subscription at a group's first frame
kixelated Oct 23, 2026
4e60f21
Merge remote-tracking branch 'origin/main' into iterate-6465
kixelated Oct 23, 2026
751df0d
Merge remote-tracking branch 'origin/main' into iterate-6465
kixelated Oct 23, 2026
a0b760d
fix(net)!: every range and fetch starts at a group's first frame
kixelated Oct 23, 2026
2b4ac58
Merge remote-tracking branch 'origin/main' into iterate-6465
kixelated Oct 23, 2026
cec4011
fix(net): drop the doc link to the private frame_start and a stale qu…
kixelated Oct 23, 2026
863e09d
Merge remote-tracking branch 'origin/main' into iterate-6465-b
kixelated Oct 24, 2026
06ad3f2
style(net): rustfmt after merging main
kixelated Oct 24, 2026
f226113
fix(net): cut lite datagrams by Object ID against both frame bounds
kixelated Oct 24, 2026
09dc81f
Merge remote-tracking branch 'origin/main' into HEAD
kixelated Oct 24, 2026
76c0f9e
Merge remote-tracking branch 'origin/main' into HEAD
kixelated Oct 24, 2026
ca444ec
Merge remote-tracking branch 'origin/main' into HEAD
kixelated Oct 24, 2026
73291be
test(net): anchor main's range test on a group sequence
kixelated Oct 24, 2026
cbf4aa1
Merge remote-tracking branch 'origin/main' into HEAD
kixelated Oct 24, 2026
8adfa3c
Merge remote-tracking branch 'origin/main' into rq-6465
kixelated Oct 24, 2026
8194a24
Merge remote-tracking branch 'origin/main' into rq-6465
kixelated Oct 24, 2026
2858200
Merge remote-tracking branch 'origin/main' into rq-6465
kixelated Oct 24, 2026
4f40045
Merge remote-tracking branch 'origin/main' into rq-6465
kixelated Oct 24, 2026
bf6ebe3
Merge remote-tracking branch 'origin/main' into rq-6465
kixelated Oct 24, 2026
30b4ff0
Merge remote-tracking branch 'origin/main' into rq-6465
kixelated Oct 24, 2026
ef5d764
test(net): format the crate-internal partial fetch in the refused-gro…
kixelated Oct 24, 2026
666c2f6
quest: subscribe-ranges names group-only absolute anchors
kixelated Oct 25, 2026
7366e8a
Merge remote-tracking branch 'origin/main' into rq-6465
kixelated Oct 25, 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
20 changes: 4 additions & 16 deletions dart/moq_ffi/lib/src/moq.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4931,22 +4931,16 @@ class FfiConverterMoqAnchor {

class AbsoluteMoqAnchor extends MoqAnchor {
final int group;
final int frame;
AbsoluteMoqAnchor({required int this.group, required int this.frame});
AbsoluteMoqAnchor._(int this.group, int this.frame);
AbsoluteMoqAnchor(int this.group);
AbsoluteMoqAnchor._(int this.group);
static LiftRetVal<AbsoluteMoqAnchor> read(Uint8List buf) {
int new_offset = buf.offsetInBytes;
final group_lifted = FfiConverterUInt64.read(
Uint8List.view(buf.buffer, new_offset),
);
final group = group_lifted.value;
new_offset += group_lifted.bytesRead;
final frame_lifted = FfiConverterUInt64.read(
Uint8List.view(buf.buffer, new_offset),
);
final frame = frame_lifted.value;
new_offset += frame_lifted.bytesRead;
return LiftRetVal(AbsoluteMoqAnchor._(group, frame), new_offset);
return LiftRetVal(AbsoluteMoqAnchor._(group), new_offset);
}

@override
Expand All @@ -4958,9 +4952,7 @@ class AbsoluteMoqAnchor extends MoqAnchor {

@override
int allocationSize() {
return FfiConverterUInt64.allocationSize(group) +
FfiConverterUInt64.allocationSize(frame) +
4;
return FfiConverterUInt64.allocationSize(group) + 4;
}

@override
Expand All @@ -4971,10 +4963,6 @@ class AbsoluteMoqAnchor extends MoqAnchor {
group,
Uint8List.view(buf.buffer, new_offset),
);
new_offset += FfiConverterUInt64.write(
frame,
Uint8List.view(buf.buffer, new_offset),
);
return new_offset;
}
}
Expand Down
2 changes: 1 addition & 1 deletion doc/concept/moq-lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ Each subscription carries the knobs that decide behavior under congestion:
| **Priority** (-128..127, default 0) | Higher-priority tracks get bandwidth first. Audio above video, base layer above enhancement. The publisher's track priority breaks ties between subscriptions at one priority. From a native Rust publisher over QUIC or WebTransport (iroh excepted), subscriptions at the same priority and publisher priority split bandwidth evenly by bytes, streams and datagrams alike, however many groups each has queued. A browser publisher and other transports without send groups are not covered. |
| **Order** | Which group to send first when several are pending. Newest first for live (the default), oldest first for catch-up. Needs moq-lite 07 or moq-transport; older moq-lite always sends newest first. |
| **Max delay** | How long a non-latest group stays worth delivering before it is skipped. Zero means "live edge only", and raising it is also what asks for history. Unset leaves the publisher's retention window alone to bound it. |
| **Ranges** | Which groups to ask for: from a group, a number of groups back from the latest, or the group containing a presentation time, optionally up to an end. The default asks for everything max delay allows. Max delay still caps every range. |
| **Ranges** | Which groups to ask for: from a group, a number of groups back from the latest, or the group containing a presentation time, optionally up to an end. Each group arrives whole, from its first frame. The default asks for everything max delay allows. Max delay still caps every range. |

On moq-lite 05 and later, a publisher explicitly reports groups that will not arrive.
This lets a finished subscription settle without waiting for missing groups.
Expand Down
9 changes: 5 additions & 4 deletions drafts/draft-lcurley-moq-lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,15 +211,15 @@ The resolved start is therefore always either exactly the requested Position or

The asymmetry with Groups is deliberate.
A Group is the unit of decodability: dropping leading *Groups* leaves a stream the application can still decode, whereas dropping leading *Frames* often does not, whether because the Group opens with a keyframe the rest depends on or because its compression state lives in the frames that went missing.
Only the subscriber knows whether a partial Group is any use to it, so only the subscriber may ask for one.
A subscription therefore starts at a Group's first frame, and a subscriber asks for a partial Group only to finish one whose earlier frames it already holds, from this publisher or another.

Frame indices are only meaningful relative to a Group the subscriber has already begun receiving, so:

- A `Frame Start` qualifies the group `Group Start` names, group 0 included.
A subscriber that has seen no such group MUST send `Frame Start` = 0, since it cannot number the frames of a group it has not received.
A subscriber MUST send `Frame Start` = 0 unless it already holds every frame of that group below it.
- A `Frame End` is meaningless without a `Group End`; an unbounded subscription (`Group End` = 0) MUST send `Frame End` = 0.

A publisher MUST close the session with a PROTOCOL_VIOLATION if either rule is violated.
A publisher MUST close the session with a PROTOCOL_VIOLATION on a non-zero `Frame End` without a `Group End`.

# Flow
This section outlines the flow of messages within a moq-lite session.
Expand Down Expand Up @@ -1271,7 +1271,7 @@ A non-zero value is the absolute group sequence + 1.
The index of the first frame to deliver within the `Group Start` group (see [Positions](#positions)).
A value of 0 means from the start of that group (default), so the group is delivered whole.
Frames before this index are not delivered, even when delivery begins at that exact group; a start resolved at a later group is always delivered from frame 0.
A subscriber that has received no group MUST send 0, since it cannot number the frames of a group it has not seen.
A subscriber MUST send 0 unless it already holds every frame below this index, from this publisher or another, since a Group is the unit of decodability (see [Positions](#positions)).

**Frame End**:
The last frame to deliver (inclusive) within the end group (see [Positions](#positions)).
Expand Down Expand Up @@ -1811,6 +1811,7 @@ The `Message Length` describes the payload size on the wire.
- Added TRACK_INFO `Refresh` and SUBSCRIBE_UPDATE `Wanted Group`, carrying ratio-based refresh demand through relays.

- Added `Compression` to TRACK_INFO: none (0x0) or DEFLATE (0x1), whose Groups are DEFLATE streams per draft-lcurley-moq-flate. FRAME and datagrams carry an `Inflated Size` when it is not none. Earlier versions receive the Track inflated.
- A subscription starts at a Group's first frame: SUBSCRIBE carries a non-zero `Frame Start` only when the subscriber holds every frame below it, compressed Track or not.

- Replaced ANNOUNCE_OK Active Count with an ordered, empty SYNCED event (0x4), marking the initial set without retaining routes that end before delivery.

Expand Down
2 changes: 1 addition & 1 deletion go/wrapper/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type (
Range = ffi.MoqRange
// Anchor is where one end of a Range falls: AnchorAbsolute, AnchorRelative, or AnchorTime.
Anchor = ffi.MoqAnchor
// AnchorAbsolute is an exact group and frame.
// AnchorAbsolute is a group, delivered from its first frame.
AnchorAbsolute = ffi.MoqAnchorAbsolute
// AnchorRelative is the group Groups before the latest one, clamped to group 0: 0 is the latest.
AnchorRelative = ffi.MoqAnchorRelative
Expand Down
2 changes: 1 addition & 1 deletion kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ typealias Subscription = uniffi.moq.MoqSubscription
typealias Range = uniffi.moq.MoqRange
/** Where one end of a [Range] falls: [AnchorAbsolute], [AnchorRelative], or [AnchorTime]. */
typealias Anchor = uniffi.moq.MoqAnchor
/** An exact group and frame. */
/** A group, delivered from its first frame. */
typealias AnchorAbsolute = uniffi.moq.MoqAnchor.Absolute
/** The group `groups` before the latest one, clamped to group 0: 0 is the latest. */
typealias AnchorRelative = uniffi.moq.MoqAnchor.Relative
Expand Down
2 changes: 1 addition & 1 deletion py/moq-rs/tests/test_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import pytest

# Every group from the first, rather than only what the live edge still reaches.
FROM_GROUP_0 = [moq.Range(start=cast(moq.Anchor, moq.Anchor.ABSOLUTE(group=0, frame=0)))]
FROM_GROUP_0 = [moq.Range(start=cast(moq.Anchor, moq.Anchor.ABSOLUTE(group=0)))]


async def until_wanted(demand: moq.TrackDemand, wanted: bool = True) -> None:
Expand Down
2 changes: 1 addition & 1 deletion py/moq-rs/tests/test_testing_clock.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ async def main():
# The origin's driver runs the timers that judge a group stale.
origin = moq.OriginProducer()
track = origin.create_broadcast("clock").publish_track("track")
from_group_0 = [moq.Range(start=moq.Anchor.ABSOLUTE(group=0, frame=0))]
from_group_0 = [moq.Range(start=moq.Anchor.ABSOLUTE(group=0))]
consumer = track.consume(moq.Subscription(max_delay=timedelta(seconds=1), ranges=from_group_0))

# Group 0 stalls with no frame, so the in-order read waits on it until
Expand Down
13 changes: 6 additions & 7 deletions quest/m1/compress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ Every interview prompt and the selected answer (✅) are in the planning PR.
parameter get the inflated copy, with no flag. A sealed compression
declaration is preserved, so delivery is refused when plain fallback would
remove it.
- **Ranges.** A range starting at frame N of a compressed group is served
from N. The requester only asks from N because it holds 0..N-1, so it
inflates from its own cached prefix. Reading frame N without 0..N-1 is a
non-goal: groups are delta encoded anyway. The requesting model enforces
it: a start on a compressed group it lacks the prefix for widens to frame
0 before the request leaves, and a server serves exactly what is asked.
- **Ranges.** Compressed tracks are not special: every subscription, new or
resumed, starts at the beginning of a group, and one resuming from another
route may take the group's start from that route's cached prefix
(maintainer, on [moq-dev/moq#6465](https://github.com/moq-dev/moq/pull/6465)). Application
ranges and fetches name whole groups, so a compressed group always
inflates from a prefix its requester holds.
- **Specs:** draft-lcurley-moq-flate keeps the codec and becomes the IETF
extension; draft-lcurley-moq-lite specifies the lite-07 field and size and
references it for the codec.
Expand All @@ -81,7 +81,6 @@ This README owns the end-to-end proof and the guide once every child lands:

## Required

- [moq-transport mid-group subscriptions widen](/quest/m1/compress/ietf-range-widen.md) - an application range starting mid-group on a compressed track widens to frame 0 at a moq-transport subscriber instead of failing with `Lagged`
- [Migrate](/quest/m1/compress/migrate.md) - catalogs, stats, and timelines publish one compressed track; moq-flate, @moq/flate, and every `.z` name are gone

## Related
Expand Down
58 changes: 0 additions & 58 deletions quest/m1/compress/ietf-range-widen.md

This file was deleted.

6 changes: 3 additions & 3 deletions quest/m1/ietf-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ them yet. Remaining, in order:
edge's TRACK_INFO query becomes TRACK_STATUS upstream, and the pinned
MoQTestPublisher inherits NOT_SUPPORTED. Preserve that refusal: widening
query-only demand into a SUBSCRIBE is not an authorized fallback.
[Range widening](/quest/m1/compress/ietf-range-widen.md), claimed by
[moq-dev/moq#6465](https://github.com/moq-dev/moq/pull/6465), owns that
separate cold-metadata boundary. Needs a Moxygen revision whose test
No cold request needs that metadata to pick its start, since
[moq-dev/moq#6465](https://github.com/moq-dev/moq/pull/6465) starts every
subscription at a group's first frame. Needs a Moxygen revision whose test
publisher answers TRACK_STATUS; then bump the pins, rebase the profile, and
add the two-relay cell back to `test/ietf-conformance/run.sh` (it ran in
[d96126c65a](https://github.com/moq-dev/moq/commit/d96126c65a52027820884cee7cb9741e79fb674b)).
Expand Down
2 changes: 1 addition & 1 deletion quest/m1/release-audit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ child records whether its behavior is new on main or inherited from release.

- [JS subscription ranges](/quest/m1/subscribe-ranges/js.md) - range lists and frame offsets build on the bounds fixed here
- [OBS video source](/quest/m2/obs-moq-video/source.md) - the decoder replacement keeps the timing and EOF behavior fixed here
- [Compressed tracks](/quest/m1/compress/README.md) - mid-group widening and application migration have existing owners
- [Compressed tracks](/quest/m1/compress/README.md) - application migration has an existing owner
- [Archive consumer](/quest/m1/archive/consumer.md) - current producer and CLI reader still need the new-format roundtrip
- [Measured release performance](/quest/m1/release-perf/README.md) - existing paired benchmark regressions and their confirmation runs
- [Transport performance audit](/quest/m2/audit-perf.md) - congestion-control coverage remains outstanding
Expand Down
3 changes: 1 addition & 2 deletions quest/m1/resolved-demand.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ with no wire change.
because watching for it needs more than this plan carries: a verdict
depends on two groups (`drifted` uses G's successor for `reach` and a
possibly later presentation edge in `live_above`, and losing either can
free G), a range may end mid-group so an edge needs a frame position, and
a run skipped at the max age bound would no longer be final. Likewise,
free G), and a run skipped at the max age bound would no longer be final. Likewise,
a stale drop in flight across an aggregate range change (lite-07, see
[Drop set](/quest/m1/drop-set.md)) may settle a reader whose own range
would not convict, since SUBSCRIBE_UPDATE has no acknowledgement and the
Expand Down
10 changes: 5 additions & 5 deletions quest/m1/subscribe-ranges/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ relay bounds a sparse FETCH only if the model can request ranges:
an IETF FETCH is served from the model's ranges, capped at the Largest
Object.
- **Relative ends** (maintainer, 2026-10-11, superseding the 10-07 `Live`
flag and closing #5085). Each range end is `Absolute(group, frame)` or
flag and closing #5085). Each range end is `Absolute(group)` or
`Relative(n)`, naming the group n before the latest; either end may be
relative, and an omitted end is live. An offset past the track's start
clamps to group 0. Before any group exists, relative ends resolve when the
Expand Down Expand Up @@ -82,9 +82,10 @@ relay bounds a sparse FETCH only if the model can request ranges:
its group starts, so a time end settles across a sequence that arrived only
as a datagram, and a stream copy of that group arriving later is read even
if it starts past the end.
- Ranges are frame-precise (`Position`), not whole groups. The IETF joining
FETCH for a mid-group SUBSCRIBE's uncached prefix stays, because today's
bridge relies on it (maintainer, 09-29).
- Ranges are whole groups: a range always starts at a group's first frame
Comment thread
coderabbitai[bot] marked this conversation as resolved.
(maintainer, on [moq-dev/moq#6465](https://github.com/moq-dev/moq/pull/6465)). The IETF joining FETCH for the
live group's uncached prefix stays, because today's bridge relies on it
(maintainer, 09-29).
- The relay half of fetch-span moves here; #4558 landed only the no-handler
skip.

Expand All @@ -98,7 +99,6 @@ every sequence delivered or dropped.
- [Lite-07 ranges](/quest/m1/subscribe-ranges/lite.md) - the lite-07 wire, Rust publisher and subscriber, and the draft
- [moq-transport ranges](/quest/m1/subscribe-ranges/ietf.md) - range FETCH upstream per missing run, and non-blocking FETCH served downstream
- [JS ranges](/quest/m1/subscribe-ranges/js.md) - `@moq/net` model and lite-07 parity
- [Bounded tails](/quest/m1/subscribe-ranges/bounded-tail.md) - a copy cut at a request's mid-group end accepts the rest of its group instead of rejecting it as `Duplicate`

## Related

Expand Down
42 changes: 0 additions & 42 deletions quest/m1/subscribe-ranges/bounded-tail.md

This file was deleted.

Loading
Loading