Repository navigation
feat(net)!: transit facts stay read-only - #8007
Conversation
Quest-Claim: 84a57a369ecd26e9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NonZero hop ids give the origin handles a niche, shrinking the handshake steps enough that clippy flags the size gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed head: 710deba
Base: 1a6ac47
Findings: No actionable regression found in this static review.
Coverage: Reviewed the Rust/JS API changes and updated callers across the 155-file diff, concentrating on nonzero local and peer identities, received zero/anonymous hops, the 32-hop cap and relay validation, lite/IETF session conversions, Rust/C/FFI route projections, the boxed Connect::Started state, JS public listing projection, and the migration/API documentation. The Rust default random-id range is preserved; larger library hop limits are clamped before use while the relay still rejects values outside 1–32. JS keeps epoch/auth in its internal route representation and returns a separate public snapshot with copied hop ids and derived anonymity, retaining internal change detection.
Direction: The internal-wire/public-view separation matches the stated API goal. Keep the documented breaking migrations and the existing loop, hop-limit, wide-id, and anonymity regression coverage. No additional code change is requested by this review.
Validation limits: GitHub-only source review. I did not execute quest context, just check, tests, compilation, benchmarks, or all-language interoperability checks. The PR reports 10,952 passing Rust tests after the boxing fix and a separately blocked benchmark-lock check; those results were not reproduced here. The exact-head Check workflow is still in progress. This COMMENT is not an approval or a claim that all validation is complete.
There was a problem hiding this comment.
ℹ️ One documentation inaccuracy, anchored inline.
Reviewed changes
- Rust hop types leave the public API.
Hop,HopsandInvalidHoparepub(crate),origin::HopLimitis gone, and applications now pass and readNonZeroU64/u64:Config::{hop, max_hops},Producer/Consumer::hop(),RouteInfo::hops()(an allocation-freeExactSizeIterator),Source::Peer(u64), and the peer-hop setters onClient,server::Handshakeand moq-tokio. - The 32-hop cap moves into the origin.
Producer::newclampsConfig::max_hops(a plainNonZeroUsize) to the wire'sMAX_HOPS, while moq-relay keeps rejecting--cluster-max-hopsoutside 1 to 32, so the CLI contract is unchanged. - JS splits the internal route from the public facts.
WireRoutekeeps epoch, auth and branded hops for the table and sessions;Origin.RouteInfois{ instance, hops: bigint[], cost, anonymous }, projected byrouteInfo()only inAnnounce.Consumer.next(). All four publicannounced()entry points (origin.ts,connection/pool.ts,lite/connection.ts,ietf/connection.ts) reach it throughannounce.list(), so the projection cannot be bypassed, and the newannounced.test.tscase pins the exact key set. - Bench/test builders shorten to plain ids. The hidden
fuzzhelpers (route_info,excluding,Announced) take ids, and the ~100 config sites in tests and benches move toNonZeroU64::new. - Bindings and docs.
moq-ffi/moq-calready exposedhops: Vec<u64>plusanonymous, so only their internals changed;MIGRATE.mdentries cover moq-net, moq-tokio and@moq/net, and the finished quest and its epic are deleted with prose links pinned to their lastmainrevision.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
…ute/facts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Applies the Pullfrog suggestion on #8007. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Source::Peer(0)doc corrected. The comment now says0means the announcing handle named no hop, covering both a peer handle announced in-process and a session whose peer declared no hop and was assigned none — the substantive concern from the prior review's two inline threads, which the author resolved.origin/mainmerged in. The one conflict resolution inrs/moq-net/MIGRATE.mdkeeps both Unreleased entries: the PR'sHop chains are plain idssection and main's newtrack::Consumer::unmeteredsection, ahead of the existingSocket readers start at the beginningentry.
The PR's own changed lines are unchanged since the previously reviewed head, and the merge replayed cleanly everywhere else (the with_peer_hop/peer_hop signatures in moq-net and moq-tokio are intact at head).
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
# Conflicts: # rs/moq-net/MIGRATE.md
Main now reserves MIGRATE.md for the release cut. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Diagnosed the Diagnosis. Fix. Quarantined the case with Could not push to this PR. The commit is ready, but the push was rejected: So the quarantine landed as a separate PR against
|
|
quest land watch: the merge queue removed this pull request at 0c3fdd0 because its The land fixer works on it in the background, and the watcher re-enqueues it at the back of the queue once it is fixed or judged flaky, or hands it back here with what it needs.
…calhost:4443/test negotiated ALPN: moq-lite-07-wip
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: publish ok: broadcast=room track=video
@moq/net test: subscribe update: id=0 broadcast=room track=video
@moq/net test: subscribe update: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=remote error: 1
@moq/net test: subscribe update: id=0 broadcast=room track=video
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test: https://localhost:4443/test negotiated ALPN: moq-lite-07-wip
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: subscribe update: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: publish ok: broadcast=room track=video
@moq/net test: subscribe update: id=0 broadcast=room track=video
@moq/net test: subscribe update: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=remote error: 1
@moq/net test: publish error: broadcast=room track=video error=session closed
@moq/net test: subscribe update: id=0 broadcast=room track=video
@moq/net test: subscribe update: broadcast=room track=video
@moq/net test: publish error: broadcast=room track=video error=session closed
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test:
@moq/net test: ::endgroup::
@moq/net test:
@moq/net test: ::group::src/lite/refresh.test.ts:
@moq/net test: https://localhost/refresh negotiated ALPN: moq-lite-06
@moq/net test: announced: prefix=
@moq/net test: announce: prefix=
@moq/net test: announce: broadcast=room active=true instance=0fdd4226efaf8139dde72073c7227e6e
@moq/net test: announced: broadcast=room active=true instance=~aa2b8866a340eb1cabcb91f77fd73f59
@moq/net test: track info: broadcast=room track=data
@moq/net test: subscribe start: id=0 broadcast=room track=data
@moq/net test: track info: broadcast=room track=data
@moq/net test: subscribe ok: id=0 broadcast=room track=data
@moq/net test: publish ok: broadcast=room track=data
@moq/net test: subscribe update: id=0 broadcast=room track=data
@moq/net test: subscribe update: broadcast=room track=data
@moq/net test: publish error: broadcast=room track=data error=session closed
@moq/net test: subscribe close: id=0 broadcast=room track=data
@moq/net test: https://localhost/refresh negotiated ALPN: moq-lite-07-wip
@moq/net test: announced: prefix=
@moq/net test: announce: prefix=
@moq/net test: announce: broadcast=room active=true instance=11aab110491adb61c0b83325838228ca
@moq/net test: announced: broadcast=room active=true instance=11aab110491adb61c0b83325838228ca
@moq/net test: track info: broadcast=room track=data
@moq/net test: subscribe start: id=0 broadcast=room track=data
@moq/net test: track info: broadcast=room track=data
@moq/net test: subscribe ok: id=0 broadcast=room track=data
@moq/net test: publish ok: broadcast=room track=data
@moq/net test: subscribe update: id=0 broadcast=room track=data
@moq/net test: subscribe update: broadcast=room track=data
@moq/net test: subscribe update: id=0 broadcast=room track=data
@moq/net test: subscribe update: broadcast=room track=data
@moq/net test: publish error: broadcast=room track=data error=session closed
@moq/net test: subscribe close: id=0 broadcast=room track=data
@moq/net test:
@moq/net test: ::endgroup::
@moq/net test:
@moq/net test: ::group::src/lite/track_stream.test.ts:
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test: subscribe start: id=0 broadcast=room track=video
@moq/net test: subscribe ok: id=0 broadcast=room track=video
@moq/net test: subscribe close: id=0 broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: fetch error: broadcast=room track=video group=0 error=The operation was aborted.
@moq/net test: track unknown: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test: track info: broadcast=room track=video
@moq/net test:
@moq/net test: ::endgroup::
@moq/net test:
@moq/net test: ::group::src/lite/tracks.test.ts:
@moq/net test: tracks failed: broadcast=room error=track listed twice: a
@moq/net test: tracks failed: broadcast=room error=unlisted track removed: a
@moq/net test: tracks failed: broadcast=room error=duplicate SYNCED
@moq/net test: tracks failed: broadcast=room error=remote error: 7 (too many listed tracks)
error: recipe `test-changed` was terminated on line 58 by signal 15
##[endgroup]
##[error]ci: test rs failed
error: recipe `ci` failed on line 115 with exit code 101
##[error]Process completed with exit code 101.
##[group]Run actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
with:
name: cargo-timings
path: target/cargo-timings/cargo-timing-*.html
if-no-files-found: ignore
retention-days: 14
compression-level: 6
overwrite: false
include-hidden-files: false
archive: true
env:
BASE: 1ae5c5f808f5fb695a42dcb451611edce81802d7
CHECK_FAIL_FAST: 1
NEXTEST_TEST_THREADS: 24
MBX_CACHE_LINKS: 1
MBX_REMOTE_URL:
##[endgroup]
With the provided path, there will be 1 file uploaded
Artifact name is valid!
Root directory input is valid!
Uploading artifact: cargo-timings.zip
Beginning upload of artifact content to blob storage
Uploaded bytes 100726
Finished uploading artifact content to blob storage!
SHA256 digest of uploaded artifact is a8e8205a194580509184f67f422cb19f63f71557bc62e7e625e4149356e02a15
Finalizing artifact upload
Artifact cargo-timings successfully finalized. Artifact ID 13537595803
Artifact cargo-timings has been successfully uploaded! Final size is 100726 bytes. Artifact ID is 13537595803
Artifact download URL: https://github.com/moq-dev/moq/actions/runs/42283341157/artifacts/13537595803
Post job cleanup.
##[start-action display=Run jdx/mr-boxington-action@88acf2bb50552c7c508cbac13e5f8c576b95b1de;id=__612f4876-bf14-4306-a508-c2344df7d64e.mbx]
##[end-action id=__612f4876-bf14-4306-a508-c2344df7d64e.mbx;outcome=skipped;conclusion=skipped;duration_ms=0]
Post job cleanup.
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git version
git version 2.55.0
Temporarily overriding HOME='/var/cache/github-runner-work/_temp/10cd2d2e-5069-4591-866b-c3d997544aa6' before making global git config changes
Adding repository directory to the temporary git global config as a safe directory
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --global --add safe.directory /var/cache/github-runner-work/moq/moq
Removing SSH command configuration
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp core\.sshCommand
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
Removing HTTP extra header
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
Removing includeIf entries pointing to credentials config files
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
[command]/nix/store/lg46w7hrjx7kylsh6645c9l975i8h43b-git-2.55.0/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
A job completed hook has been configured by the self-hosted runner administrator
##[group]Run '/nix/store/gx2v6q84b0w43bafm0whkdx93qyxbpps-ci-cache-gc.sh/bin/ci-cache-gc.sh'
shell: /nix/store/1mv3qz005gkbalxfghk4y3s8ayv4d2dl-bash-interactive-5.3p15/bin/bash --noprofile --norc -e -o pipefail {0}
##[endgroup]
evicted 658 objects and 91 action results (12.7 GiB logical); 49.8 GiB logical remain
dropped 11 session event streams
Cleaning up orphan processes
(written by quest land watch) |
|
codex / Codex land fixer note:
(written by codex / Codex) |
|
quest land watch: armed auto-merge on this pull request at 24c6150, so it joins the back of the merge queue once its required checks pass, with the land fixer's fix ( (written by quest land watch) |
There was a problem hiding this comment.
Important
just bench-fanout no longer builds on the current tree. The out-of-workspace bench/fanout/session harness still imports the now-private moq_net::Hop, so its build fails and bench/run.sh aborts the whole fan-out run for the current tree.
Reviewed changes
- Merged the newer
main.08dcd58eamerges1ae5c5f8conflict-free: the threeMIGRATE.mdfiles stay byte-identical tomain(so main's newFill::wanted_changedentry arrives without resurrecting this PR's removed notes), and the only shared-file change is main'serr.retryable()route-failure path. - Migrated the merged test origins.
24c6150moves the sixorigin::Config::new(crate::Hop::new(1).unwrap())calls that the merge reintroduced inietf::publisher,lite::publisherandlite::subscribertests tostd::num::NonZeroU64::new(1).unwrap(), matching the PR's API.
⚠️ The fan-out session harness no longer compiles
bench/fanout/session/src/main.rs still does use moq_net::{… Hop …} and origin::Config::new(Hop::new(hop).unwrap()). Hop is crate-private in this PR, so cargo check there fails with error[E0603]: struct 'Hop' is private. bench/run.sh reports a base revision's build failure as unsupported, but a failure in the current tree is fatal, so just bench-fanout (default scenarios model session) stops before measuring anything. This break came with the API change itself rather than the last two commits, but it is the one consumer the migration missed.
Technical details
# Fix the out-of-workspace fan-out harness after `Hop` became private
## Affected sites
- `bench/fanout/session/src/main.rs:11` — `use moq_net::{Client, Hop, Server, ...}` no longer resolves; `Hop` is `pub(crate)` (`rs/moq-net/src/model/mod.rs:88`).
- `bench/fanout/session/src/main.rs:16` — `origin::Config::new(Hop::new(hop).unwrap())` must pass a `NonZeroU64`.
- `bench/run.sh:1133-1137` — a failed build under `$RUN/fanout/current/` prints the log and `return 1`; `bench/run.sh:142` defaults `FANOUT_SCENARIOS` to `model session`.
- `bench/run.sh:1108-1120` rewrites the harness's `../../../rs/` paths to the checkout under test and copies that revision's `Cargo.lock`, so the harness is expected to build against this revision's `moq-net`.
## Required outcome
- `bench/fanout/session` builds against this revision's `moq-net`, so `just bench-fanout` once again measures the `session` scenario on the current tree.
- No other file under `bench/` uses the removed hop API; `bench/fanout/{group,model}` are clean.
## Suggested approach
- Drop `Hop` from the `moq_net` import and use `origin::Config::new(std::num::NonZeroU64::new(hop).unwrap())`, matching the other migrated call sites.
## Verification
- `cd bench/fanout/session && cargo check --offline` reproduced `E0603: struct 'Hop' is private` at `24c6150`.deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
…iber tests Main's #8007 changed the argument from Hop; these two tests came from this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Problem
Hops, via, source, the epoch and the anonymous mark are facts the origin and the relays fill in, but
Hop,Hops,InvalidHopandorigin::HopLimitwere still public in Rust, and@moq/netstill exportedHop,HopSchema,isAnonymousand aRouteInfocarryingepochandauth. Quest:quest/m1/api-audit/route/facts.md, split from moq-dev/moq#7806.Approach
Hop,HopsandInvalidHopare crate-private andHopLimitis gone. Applications pass and read plain ids:origin::Config::hopis aNonZeroU64(random by default),Config::max_hopsaNonZeroUsize(above 32 acts as 32, the wire's own cap),Producer::hop()/Consumer::hop()returnNonZeroU64,RouteInfo::hops()iteratesu64ids, andSource::Peerholds au64. The peer-hop setters onClient,server::Handshakeand moq-tokio take aNonZeroU64. The hiddenfuzzhelpers (route_info,excluding,Announced) take plain ids, which shortens every bench and test that builds a chain. TheNonZeroU64ids give the origin handles a niche, which shrankClient's handshake steps enough for clippy'slarge_enum_variantto flag the internalConnectfuture; itsStartedvariant is now boxed.WireRoute(epoch, auth, branded hops). The publicOrigin.RouteInfois{ instance, hops: bigint[], cost, anonymous }, projected once where a listing hands an event to the application (Announce.Consumer.next).Hop,HopSchemaandisAnonymousleave theOriginnamespace.doc/lib/rs/moq-net.md. The migration notes live in Impact below: main now reservesMIGRATE.mdfor the release cut (moq-dev/moq#8024), so this PR no longer edits it.route/, whose last child this was; prose links to the epic now point at its last version onmain.Impact
moq-net (breaking):
Hop,Hops,InvalidHop,origin::HopLimitremoved from the public API. MigrateConfig::new(Hop::new(id)?)toConfig::new(NonZeroU64::new(id)?),HopLimit::new(n)?toNonZeroUsize::new(n)?,hop().id()tohop().get(),route.hops().iter().map(|h| h.id())toroute.hops(). Migration note for the release cut:origin::Config::new(Hop::new(id)?)origin::Config::new(NonZeroU64::new(id).context("…")?)origin::Config::new(Hop::random())origin::Config::default(), which picks a random idconfig.max_hops = origin::HopLimit::new(n)?config.max_hops = NonZeroUsize::new(n).context("…")?; above 32 acts as 32origin.hop().id()origin.hop().get()route.hops().iter().map(|hop| hop.id())route.hops(), an iterator ofu64ids where 0 is an anonymous hoporigin::Source::Peer(hop)with aHoporigin::Source::Peer(id)with au64Client::with_peer_hop(hop),server::Handshake::with_peer_hop(hop)NonZeroU64server::Handshake::peer_hop()returnsOption<Hop>Option<NonZeroU64>moq-net (breaking):
origin::Source::Peerholds au64;Client::with_peer_hopandserver::Handshake::with_peer_hoptakeNonZeroU64;server::Handshake::peer_hopreturnsOption<NonZeroU64>.moq-tokio (breaking):
Client::with_peer_hopandserver::Request::with_peer_hoptake aNonZeroU64, andserver::Request::peer_hopreturnsOption<NonZeroU64>, sincemoq_net::Hopis no longer public. Pass the id you passed toHop::new.@moq/net(breaking):Origin.Hop,Origin.HopSchema,Origin.isAnonymousremoved;Origin.RouteInfodropsepochandauth, itshopsarebigint[], and it gainsanonymous. ReplaceOrigin.isAnonymous(event.route)withevent.route.anonymous;epochandauthstay inside the origin, as in Rust.moq-relay: no change in behavior;
--cluster-idand--cluster-max-hopsvalidate as before.moq-ffi, moq-c and wrappers: none (they already exposed plain ids).
Wire: none.
Choices made without asking
Config::hopisNonZeroU64andConfig::max_hopsisNonZeroUsize, so the reserved 0 cannot be configured; amax_hopsabove 32 acts as 32, since no wire carries a longer path. The relay keeps rejecting values outside 1 to 32 itself.Producer::hop()andConsumer::hop()stay public, returningNonZeroU64: the relay'sCluster::idand the FFI and C tests read them.RouteInfo::hops()returnsimpl ExactSizeIterator<Item = u64>rather than aVec, so reading it allocates nothing.Source::Peer(0)means the announcing handle named no hop: a peer-linked handle in-process, or a session whose peer declared no hop and was assigned none (Pullfrog's suggested wording).Client,server::Handshakeand moq-tokio; they tookHop, so the Goal moves them toNonZeroU64too.quest/m1/api-audit/README.mdlost its last child, so it is now sized[M], with its own work spelled out as a final API diff againstrelease, asrelease-perfdid when its children landed.Alternatives
HopLimit-like newtype: it would leave a hop type in the public API, which the quest removes.epochandauthbehind awireOflookup on each publicRouteInfoobject: route objects live in deep-equal signals, so a field kept outside the object would stop an epoch-only change from notifying. A separate internal type, projected at the listing, avoids that.Validation
origin/mainf48bc94):quest gate just check origin/mainpassed every lane, includingtest rs,test jsandtest gh.quest check --base origin/mainpasses.origin/maind505e96 resolved one conflict inrs/moq-net/MIGRATE.mdby keeping both entries; moq-dev/moq#8024 then reservedMIGRATE.mdfor the release cut, so 0c3fdd0 restores all threeMIGRATE.mdfiles tomainand the notes moved into Impact. No new main code used the changed APIs outside what the merge already compiled.Review
mechanical fix by codex / Codex:
Merged base and migrated six test origin constructors from Hop to NonZeroU64, retaining hop ID 1; passed LIBCLANG_PATH=/nix/store/5ajx1np9zlq7z6ljqq18sibpzhnjakdw-clang-22.1.8-lib/lib cargo nextest run --locked --tests --examples --benches -p moq-audio -p moq-c -p moq-crypto -p moq-ffi -p moq-mux -p moq-net -p moq-net-tests -p moq-relay -p moq-room -p moq-tokio --features 'moq-net/async moq-net/fuzz moq-mux/fuzz moq-crypto/e2ee moq-crypto/sign moq-relay/test-support moq-tokio/driver moq-tokio/ne(head 24c6150)Codex (
quest review), gpt-6.1-sol at medium effort, round 1 on head 9fa4c59 (the hand-resolved merge of d505e96): no findings.Head 0c3fdd0 adds only a conflict-free merge of f48bc94 and a commit restoring the three
MIGRATE.mdfiles tomain(deleting this PR's notes, no code); review skipped as too trivial to need one.Pullfrog's two threads on
Source::Peer(0)(r4490770881, r4490770917): fixed by applying its suggested patch unchanged in 0dcd78f.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code