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/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ lite-07 stream type here.
- [Resumed close](/quest/m1/resume-closed.md) - a resumed JS track keeps its declared end across copies, and its `closed` and `largest()` settle on a clean end
- [Replica resume](/quest/m1/replica-resume.md) - a lagging same-epoch replica resumes with every frame exactly once and in order, on lite-07 and on moq-transport
- [Rust resume holds a declared end](/quest/m1/resume-rust-end.md) - a resumed Rust track keeps a declared end across copies, dedupes flapping copies, and judges retention inside the reader's window
- [Rust resume checks compression](/quest/m1/resume-compress.md) - a same-epoch route copy with a different `compress` is refused instead of corrupting framing
- [Rust resume backs single-route groups](/quest/m1/resume-handed-out.md) - a group first read from one route keeps the later copy that holds it, with a passthrough benchmark
- [WebGPU on Safari](/quest/m1/webgpu-safari.md) - the WebGPU renderer is verified on Safari 26 for macOS and iOS
- [Interop finish](/quest/m1/interop-finish.md) - the interop matrix proves a clean broadcast finish across implementations
- [Exporters follow a rendition to a new track](/quest/m1/export-rendition-rebind.md) - the fMP4, TS, MKV and FLV exporters rebuild a rendition whose resolved `track` or `broadcast` changes under the same ID
Expand Down
31 changes: 31 additions & 0 deletions quest/m1/resume-compress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# [XS] Rust resume refuses a route copy with different compression

## Goal

A same-epoch route whose track declares a different `compress` than the
logical track's first answer is refused before any of its groups are
forwarded, as JS already does. Today a compressed route A replaced by an
uncompressed route B under the same epoch keeps A's metadata while serving B's
groups, so a downstream lite-07 reader fails on the next group with a wrong
frame size.

## Plan

Found by an external audit on 2026-10-22, confirmed on main: `Front::track_info`
(`rs/moq-net/src/model/front.rs`) compares timescale, max age, and priority,
not `compress`. JS's `compatible()` (`js/net/src/resume.ts`) compares all four.

- Add `compress` to the comparison. A mismatch takes the existing refusal
path, with the same classification as the other mismatched fields.
- `refresh` stays unchecked on both sides: it is a join heuristic, not
framing.
- Regression test through mocked sessions in
`rs/moq-net/tests/integration/route_change.rs`: compressed A serves a group,
uncompressed B takes over under the same epoch, and the reader never sees
B's groups decoded under A's metadata. Matching compression keeps resuming.

Public API: behavior only. Wire: none.

## Related

- [Rust resume holds a declared end](/quest/m1/resume-rust-end.md) - the other Rust copy-compatibility check
40 changes: 40 additions & 0 deletions quest/m1/resume-handed-out.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# [S] Rust resume keeps a backup for a group read from a single route

## Goal

A Rust `resume::Reader` keeps a replaced copy that holds any group it handed
out and is still reading, including a group first read while only one copy
existed. Today such a group is never recorded, so the copy that later holds it
is dropped and the read waits out the reader's `max_delay` before ending with
`Error::Old`, even though the missing frames were available.

## Plan

Found by an external audit on 2026-10-22, confirmed on main. `hand_out`
(`rs/moq-net/src/model/resume.rs`) pushes onto `out` only when
`copies.len() > 1`, a guard from
[#6141](https://github.com/moq-dev/moq/pull/6141). Reproduction: read the
first frame of group 0 from A; swap to B, which holds all of group 0; B's
route fails, keeping its finished group; swap to C, which lacks group 0, and
read group 1; A's partial group 0 fails. B was dropped, so group 0 stalls.

- Decided 2026-10-22 (maintainer): record every handed-out group, dropping the
guard, as [#6136](https://github.com/moq-dev/moq/pull/6136) does in JS. The
existing sweep keeps the list at the live group count.
- Decided 2026-10-22 (maintainer): add a passthrough benchmark in
`rs/moq-net/benches` that reads groups through a resumed track with a
single copy, so the per-group record's cost shows up.
- Regression test on a mocked clock for the reproduction above.

Lands after [#6224](https://github.com/moq-dev/moq/pull/6224), which edits the
same reader, so it does not widen a PR under review.

Public API: behavior only. Wire: none.

## Required

- [Rust resume holds a declared end](/quest/m1/resume-rust-end.md) - lands first; it edits the same reader

## Related

- [Resumed close](/quest/m1/resume-closed.md) - carries the JS half of this fix
Loading