Skip to content

fix(moq-mux): keep catalog Consumer Clone + stable FramedFormat discriminants#1661

Merged
kixelated merged 3 commits into
mainfrom
claude/breaking-changes-review-x69s11
Jun 9, 2026
Merged

fix(moq-mux): keep catalog Consumer Clone + stable FramedFormat discriminants#1661
kixelated merged 3 commits into
mainfrom
claude/breaking-changes-review-x69s11

Conversation

@kixelated

@kixelated kixelated commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

cargo-semver-checks flagged moq-mux as API-breaking in the release-plz PR (#1618). This walks back the avoidable breaks so the crate can ship as a patch (0.5.3 → 0.5.4) instead of a minor bump.

  • Restore Clone on the catalog Consumer. It lost Clone when feat(moq-json): JSON Merge Patch snapshot/delta helper, route hang catalog through it #1655 rewrote it to wrap moq_json::Consumer<Catalog>. Every field of that struct is already Clone, so this adds a manual impl<T> Clone for moq_json::Consumer<T> (manual rather than #[derive] to avoid a spurious T: Clone bound, since T only lives in PhantomData<fn() -> T>) and re-derives Clone on the moq-mux side. A cloned consumer inherits the current reconstruction state and gets independent TrackConsumer/GroupConsumer cursors that "inherit this offset, but then run in parallel" (group.rs).
  • Reorder FramedFormat to keep discriminants stable. feat(moq-mux): add VP8 and VP9 codec modules (import + fMP4 export) #1625 inserted Vp8/Vp9 in the middle of the enum, shifting the implicit discriminants of Aac/Opus/Mkv/Ts (5..8 → 7..10). Moving the new variants to the end restores the prior values; a comment notes new variants should be appended.
  • Bump moq-mux to 0.5.4 (manual patch).

What's intentionally left breaking

  • moq-mux: the removed public track field on the catalog Consumer stays gone. It was never a sensible part of the API and nobody should depend on it, so it ships under the patch bump.
  • moq-native: its semver changes are kept as-is (the removed TLS/iroh/websocket types are internal plumbing). No changes here.

Test plan

  • cargo test -p moq-json — added cloned_consumer_reconstructs_independently, which clones a consumer mid-group and asserts both copies apply a later delta on their own reconstruction state
  • cargo test -p moq-mux --lib (175 passed, incl. FramedFormat reorder)
  • cargo build -p moq-json -p moq-mux compiles cleanly

(Written by Claude)

claude added 2 commits June 9, 2026 00:52
The catalog Consumer lost its Clone impl when it was rewritten to wrap
moq_json::Consumer in #1655. Add a manual Clone impl to moq_json::Consumer
(every field is already Clone; manual to avoid a spurious T: Clone bound
since T only lives in PhantomData) and re-derive Clone on the moq-mux
catalog Consumer.

A cloned consumer inherits the current reconstruction state and gets
independent track/group cursors that advance in parallel.
… 0.5.4

#1625 inserted Vp8/Vp9 in the middle of FramedFormat, shifting the implicit
discriminants of Aac/Opus/Mkv/Ts (5..8 -> 7..10) and tripping
cargo-semver-checks. Move the new variants to the end so existing
discriminants stay put, and add a note so future variants are appended too.

With the discriminant shift gone and Clone restored, the only remaining API
break is the removal of the public `track` field on the catalog Consumer.
That field was never a sensible part of the API, so ship this as a patch
(0.5.3 -> 0.5.4) rather than a minor bump.
@kixelated kixelated changed the title feat(moq-mux): restore Clone on catalog Consumer fix(moq-mux): keep catalog Consumer Clone + stable FramedFormat discriminants Jun 9, 2026
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: accde689-e68b-4fbd-b1f0-12125efd8249

📥 Commits

Reviewing files that changed from the base of the PR and between a19a108 and eac8fab.

📒 Files selected for processing (1)
  • rs/moq-json/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rs/moq-json/src/lib.rs

Walkthrough

This PR adds Clone implementations to Consumer types and updates moq-mux. In rs/moq-json, Consumer gains a manual Clone that shares the TrackConsumer and duplicates reconstruction state (group, current, frames_read). In rs/moq-mux/catalog/hang, the Consumer wrapper now derives Clone. In rs/moq-mux/src/import.rs, FramedFormat variants were reordered (Vp8/Vp9 moved to the end) and matching FromStr/Display arms were updated. The rs/moq-mux crate version was bumped to 0.5.4.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly reflects the main changes: restoring Clone for the catalog Consumer and keeping FramedFormat discriminants stable.
Description check ✅ Passed The description is well-detailed and clearly related to the changeset, explaining the motivations, implementations, and trade-offs for each change included in the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/breaking-changes-review-x69s11

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
rs/moq-mux/src/catalog/hang/consumer.rs (1)

11-14: ⚡ Quick win

Consider adding a test for Clone behavior.

While the derive is correct, a test verifying that cloned catalog consumers work independently would strengthen the test suite. As per coding guidelines, tests should be added where they're easy to write, and this would be straightforward given the existing test infrastructure in this file.

🧪 Example test structure
#[test]
fn cloned_consumer_reads_independently() {
    let mut track = Catalog::default_track().produce();
    let mut consumer1 = Consumer::new(track.consume());
    let waiter = kio::Waiter::noop();
    
    let (catalog, payload) = catalog_payload("test");
    let mut group = track.append_group().expect("group should append");
    group.write_frame(payload).expect("frame should write");
    group.finish().expect("group should finish");
    
    let mut consumer2 = consumer1.clone();
    
    // Both consumers should read the same catalog independently
    assert_eq!(expect_catalog(consumer1.poll_next(&waiter)), catalog);
    assert_eq!(expect_catalog(consumer2.poll_next(&waiter)), catalog);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-mux/src/catalog/hang/consumer.rs` around lines 11 - 14, Add a unit
test that verifies Consumer (the #[derive(Clone)] struct wrapping
moq_json::Consumer<Catalog>) clones produce independent readers: create a
Catalog::default_track(), produce and append a group with catalog payload using
catalog_payload, instantiate consumer1 via Consumer::new(track.consume()), clone
it to consumer2, and assert both consumer1.poll_next(&waiter) and
consumer2.poll_next(&waiter) return the same expected catalog (use
expect_catalog to compare); place the test alongside other tests in this file to
exercise Consumer::clone and poll_next behavior.

Source: Coding guidelines

rs/moq-json/src/lib.rs (1)

227-239: ⚡ Quick win

Consider adding a test for Clone semantics.

The Clone implementation is correct, but a test verifying that cloned consumers can reconstruct values independently would be valuable and straightforward to add. As per coding guidelines, Rust tests are integrated within source files and should be added where they're easy to write.

🧪 Example test structure
#[test]
fn cloned_consumer_reconstructs_independently() {
    let config = Config { delta_ratio: Some(100.0) };
    let (mut producer, track) = producer(config);
    let mut consumer1 = Consumer::<Value>::new(track);
    let waiter = kio::Waiter::noop();
    
    producer.update(&json!({ "a": 1 })).unwrap();
    let _ = consumer1.poll_next(&waiter); // Read snapshot
    
    let mut consumer2 = consumer1.clone(); // Clone mid-stream
    
    producer.update(&json!({ "a": 2 })).unwrap();
    producer.finish().unwrap();
    
    // Both consumers should independently reconstruct the same final value
    let val1 = consumer1.poll_next(&waiter);
    let val2 = consumer2.poll_next(&waiter);
    assert_eq!(val1, val2);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-json/src/lib.rs` around lines 227 - 239, Add a unit test next to the
Consumer implementation that verifies Clone semantics: create a producer and a
Consumer::<Value>::new(track), let the producer publish a snapshot, clone the
consumer using the existing Clone impl for Consumer<T>, then publish another
update and finish the producer; call Consumer::poll_next (using a
kio::Waiter::noop()) on both the original and cloned consumers and assert the
reconstructed values are equal. Reference symbols: Consumer<T> Clone impl,
Consumer::new, Consumer::poll_next, producer.update, and producer.finish; the
test should live in the same source file as the impl and use the same helper
producer(...) used in other tests.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@rs/moq-json/src/lib.rs`:
- Around line 227-239: Add a unit test next to the Consumer implementation that
verifies Clone semantics: create a producer and a Consumer::<Value>::new(track),
let the producer publish a snapshot, clone the consumer using the existing Clone
impl for Consumer<T>, then publish another update and finish the producer; call
Consumer::poll_next (using a kio::Waiter::noop()) on both the original and
cloned consumers and assert the reconstructed values are equal. Reference
symbols: Consumer<T> Clone impl, Consumer::new, Consumer::poll_next,
producer.update, and producer.finish; the test should live in the same source
file as the impl and use the same helper producer(...) used in other tests.

In `@rs/moq-mux/src/catalog/hang/consumer.rs`:
- Around line 11-14: Add a unit test that verifies Consumer (the
#[derive(Clone)] struct wrapping moq_json::Consumer<Catalog>) clones produce
independent readers: create a Catalog::default_track(), produce and append a
group with catalog payload using catalog_payload, instantiate consumer1 via
Consumer::new(track.consume()), clone it to consumer2, and assert both
consumer1.poll_next(&waiter) and consumer2.poll_next(&waiter) return the same
expected catalog (use expect_catalog to compare); place the test alongside other
tests in this file to exercise Consumer::clone and poll_next behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fcabf70f-4f7f-4f34-9557-3e5f8981fa75

📥 Commits

Reviewing files that changed from the base of the PR and between ffa018e and 0486978.

📒 Files selected for processing (2)
  • rs/moq-json/src/lib.rs
  • rs/moq-mux/src/catalog/hang/consumer.rs

A clone taken mid-group must carry its own reconstruction state and an
independent cursor, then apply a later delta on top of its own copy. Covers
the manual Clone impl added in this PR.
@kixelated
kixelated merged commit da931f7 into main Jun 9, 2026
1 check passed
@kixelated
kixelated deleted the claude/breaking-changes-review-x69s11 branch June 9, 2026 01:27
This was referenced Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants