Skip to content

feat(moq-ffi): expose broadcast hop chain on announcements#1888

Merged
kixelated merged 3 commits into
mainfrom
claude/nifty-visvesvaraya-97880e
Jun 23, 2026
Merged

feat(moq-ffi): expose broadcast hop chain on announcements#1888
kixelated merged 3 commits into
mainfrom
claude/nifty-visvesvaraya-97880e

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

Adds a way to read a broadcast's relay hop chain from an announcement, surfaced through the Python moq package.

  • rs/moq-ffi: MoqAnnouncement::hops() -> Vec<u64> returns the broadcast's origin-id hop chain, oldest first. The ids are read from the subscribed broadcast (BroadcastConsumer derefs to Broadcast, whose hops: OriginList is populated from the wire Announce). The announce itself (OriginAnnounce = (PathOwned, Option<BroadcastConsumer>)) doesn't carry hops, so they're captured off the broadcast at construction.
  • py/moq-rs: new Announcement.hops -> list[int] property mirroring path/broadcast. Vec<u64> maps to list[int] via uniffi.
  • doc/lib/py/moq-rs.md: documents announcement.hops in the discovery section (Cross-Package Sync).

Why

A downstream voice-agent worker self-elects which relay edge answers a client by inspecting the hop chain (counting how many hops share the fleet's internal-relay prefix). The Python Announcement previously exposed only .path and .broadcast, so the worker couldn't see hops. The worker reads it as getattr(ann, "hops", None), so the property is named hops and returns a list[int] of u64 origin ids, oldest first.

Release / versioning

This rides the next moq-rs release, so the version bumps are included:

  • moq-ffi 0.2.23 -> 0.2.24 (the first release carrying hops()). This is exactly the patch release-plz would compute from an additive feat, so it should reconcile and generate the 0.2.24 CHANGELOG on merge. moq-ffi's deps are all at versions already published in chore: release #1795, so there's no stale-dependency hazard.
  • moq-rs 0.3.0 -> 0.3.1 (republishes the wrapper; 0.3.0 is already on PyPI and release-py.yml gates on the registry).
  • moq-rs dependency floor tightened moq-ffi ~= 0.2.16 -> ~= 0.2.24 so the Announcement.hops property can't resolve against a moq-ffi that lacks hops().

Cross-Package Sync

  • swift/kt/go get hops() automatically — they're uniffi-generated from moq-ffi and re-mirrored on the next moq-ffi-v* tag, no manual edit.
  • rs/libmoq (hand-written C API) intentionally left out of scope to keep this minimal and Python-focused; the C moq_announced struct can grow a hops accessor as a follow-up if a C consumer needs it.

Test plan

  • cargo check -p moq-ffi / cargo clippy -p moq-ffi clean (moq-ffi v0.2.24).
  • just py check: ruff + format + pyright (0 errors); the tightened ~= 0.2.24 floor resolves against the workspace moq-ffi 0.2.24.
  • just py test: 38 passed, including the new test_announcement_hops_over_wire (Server + Client over loopback TLS).
  • Verified against a local relay: ann.hops returns [1840552095890056] — a list of int; getattr(ann, "hops", None) returns it.

Note: fmt/clippy ran on the system toolchain (rustfmt 1.9.0) since the nix dev shell can't be entered in this environment; flag for CI's pinned toolchain.

(Written by Claude)

kixelated and others added 3 commits June 23, 2026 09:33
Add MoqAnnouncement::hops() returning the broadcast's origin-id hop chain
(oldest first) as Vec<u64>, surfaced through the Python moq wrapper as the
Announcement.hops property (list[int]). Lets consumers inspect the relay
path a broadcast traversed, e.g. for edge-affinity routing.

Bump moq-rs to 0.3.1 to republish the wrapper with the new property.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
moq-ffi 0.2.24 is the first release carrying MoqAnnouncement::hops().
Tighten the moq-rs dependency floor to moq-ffi ~= 0.2.24 so the
Announcement.hops property can't resolve against a moq-ffi that lacks
hops().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 23, 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: ee768d3d-3bb0-4a52-ae8b-63fb3db7e7e0

📥 Commits

Reviewing files that changed from the base of the PR and between 6ddfeed and 3a07be7.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • doc/lib/py/moq-rs.md
  • py/moq-rs/moq/origin.py
  • py/moq-rs/pyproject.toml
  • py/moq-rs/tests/test_server.py
  • rs/moq-ffi/Cargo.toml
  • rs/moq-ffi/src/origin.rs

Walkthrough

A new hops field is added to MoqAnnouncement in the Rust UniFFI crate (rs/moq-ffi), populated from relayed broadcast hop origin IDs in MoqAnnounced::next() and exposed via a hops() accessor returning Vec<u64> in oldest-first order. The Python wrapper (py/moq-rs) adds a corresponding Announcement.hops property returning list[int]. An integration test (test_announcement_hops_over_wire) verifies the field is non-empty after a broadcast crosses a session. Documentation is updated with a usage example and field description. The moq-ffi crate version is bumped to 0.2.24 and py/moq-rs to 0.3.1 with its moq-ffi dependency floor raised to ~= 0.2.24.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.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 accurately reflects the main change: exposing broadcast hop chain on announcements through the moq-ffi FFI layer.
Description check ✅ Passed The description is comprehensive and clearly related to the changeset, detailing the implementation across multiple packages, versioning rationale, and testing.
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/nifty-visvesvaraya-97880e

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.

@kixelated kixelated merged commit d66b3de into main Jun 23, 2026
1 check passed
@kixelated kixelated deleted the claude/nifty-visvesvaraya-97880e branch June 23, 2026 16:50
@moq-bot moq-bot Bot mentioned this pull request Jun 23, 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.

1 participant