Skip to content

refactor(net)!: concrete u64 varint codec for rs2ts - #4463

Merged
kixelated merged 10 commits into
quest/m1/rs2ts/READMEfrom
quest/m1/rs2ts/varint-codec
Sep 29, 2026
Merged

kixelated merged 10 commits into
quest/m1/rs2ts/READMEfrom
quest/m1/rs2ts/varint-codec

Conversation

@kixelated

@kixelated kixelated commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Implements quest/m1/rs2ts/varint-codec.md and deletes it.

Problem

moq-net's codec was generic traits (Encode<V>/Decode<V> over B: Buf/W: BufMut) implemented on u64, usize, u32, u16, u8, i8, bool, String, &str, Cow<str>, Vec<u8>, Bytes, Option<u64>, Duration, &[T] and Arc<T>. rs2ts cannot map generic traits on primitives without dictionary passing.

Approach

  • coding::Decoder<'a> reads from a &[u8]; coding::Encoder<'a> appends to a Vec<u8>. Both carry the varint form (QUIC or leading-ones, picked from the version once) and expose the primitives as inherent methods: varint, varint_opt, u8, u16, bool, bytes, string, slice, sub/rest.
  • Varint is a wire encoding, not a type: values are plain u64 everywhere. Encoder::varint(u64) and Decoder::varint() -> u64; the stream Reader/Writer gain varint/varint_maybe/varint_peek (and poll_*) and buffer_varint/varint. No primitive implements Encode/Decode.
  • The full u64 range: the QUIC form (moq-lite, drafts 14-16) fails with BoundsExceeded past varint::MAX_QUIC (2^62 - 1); the leading-ones form (draft-17+) carries all 64 bits. The codec splits a u64 into two u32 halves in one place, so the generated TypeScript never needs 64-bit bitwise math. The form is chosen per lite::Version, so lite-07 can switch to leading-ones (feat(lite): moq-lite-07 switches to 64-bit leading-ones varints #4455) with a one-line change.
  • Size prefixes are reserved ahead of the body (Encoder::prefix_varint/prefix_u16 return a Prefix, fill sizes it) instead of encoding every message twice through the deleted Sizer. A varint prefix reserves one byte, so a body under 64 bytes never moves.
  • lite::Parameters and ietf::Parameters are Vec-backed, so their encoding is deterministic; the fuzz round trip now checks byte-stability everywhere instead of skipping parameter maps.
  • Message decode no longer branches on tracing::enabled!; it decodes a bounded sub-decoder and logs the result afterwards.
  • IETF control framing ([type][u16 size][body]) reads through a new ietf::Body, and lite::Datagram::decode(Bytes) keeps the payload zero-copy.
  • New codec Criterion bench (cargo bench -p moq-net --features fuzz --bench codec) covers a lite SUBSCRIBE / SUBSCRIBE_UPDATE / SUBSCRIBE_START / TRACK_INFO / GROUP mix, an IETF SUBSCRIBE / SUBSCRIBE_OK / GROUP header mix (strings, parameters, small and 8-byte varints), and raw varints in both forms.

Benchmarks

cargo bench -p moq-net --features fuzz --bench codec, three builds run interleaved on one machine for three rounds, median of the three. The 1-minute load stayed between 1.05 and 1.26 for every segment. Both base and this PR pin lite-06 (QUIC) and draft-20 (leading ones); codec_varint times are per 1,024 varints.

Benchmark base a508c2dd0 previous revision 526b82d89 this PR
codec_messages/encode/lite 108.6 ns 96.4 ns 90.0 ns
codec_messages/decode/lite 249.5 ns 206.3 ns 165.2 ns
codec_messages/encode/ietf 218.7 ns 132.6 ns 113.5 ns
codec_messages/decode/ietf 328.4 ns 255.3 ns 245.4 ns
codec_varint/encode/quic 1.99 µs 1.04 µs 1.00 µs
codec_varint/decode/quic 887.2 ns 862.6 ns 802.2 ns
codec_varint/encode/leading_ones 4.99 µs 1.23 µs 1.21 µs
codec_varint/decode/leading_ones 6.49 µs 1.64 µs 1.64 µs

Dropping the VarInt newtype first made the raw varint rows ~2x slower than 526b82d89. The cause, from the disassembly of fuzz::decode_varints: at 526b82d89 LLVM inlined Decoder::varint into the loop (197 instructions, a jump table per varint); after the change it stayed an out-of-line call per varint whose Result<u64, DecodeError> came back through memory (53 instructions, a call in the loop). Nothing in the source asked for either; the inlining heuristic flipped. The fix makes the varint path inline(always) natively, from Encoder::varint/Decoder::varint down, and replaces the QUIC read's match on the tag with an if-chain, which LLVM laid out as a jump table that measured ~35% slower on the mixed-length stream. Variants, same machine and method (the first two columns from an earlier interleaved run):

Benchmark #[inline], match inline(always), match inline(always), if-chain (this PR)
codec_messages/encode/lite 98.1 ns 92.1 ns 90.0 ns
codec_messages/decode/lite 232.9 ns 177.3 ns 165.2 ns
codec_messages/encode/ietf 127.5 ns 115.8 ns 113.5 ns
codec_messages/decode/ietf 240.7 ns 223.4 ns 245.4 ns
codec_varint/encode/quic 1.94 µs 1.02 µs 1.00 µs
codec_varint/decode/quic 2.60 µs 1.17 µs 802.2 ns
codec_varint/encode/leading_ones 2.34 µs 1.23 µs 1.21 µs
codec_varint/decode/leading_ones 3.14 µs 1.66 µs 1.64 µs

Forcing the inline everywhere grew moq-wasm from 641 KB to 706 KB gzipped, because its opt-level = "z" profile was otherwise declining it at every call site. So the attribute is inline(always) only off wasm32; wasm keeps LLVM's heuristics and its size.

Build size, moq-wasm at --profile wasm-release for wasm32-unknown-unknown (before wasm-bindgen; both protocols, so the lite codec alone is smaller):

base a508c2dd0 this PR Change
raw 2,178,085 B 2,106,012 B -3.3%
gzip -9 652,957 B 641,071 B -1.8%

The existing announce bench, measured at 526b82d89 (only the newtype and the inlining changed since):

Benchmark Before After Change
announce_health/churn_lite06/100 189.3 ns 113.7 ns -40%
announce_health/churn_lite07/100 1.14 µs 971.4 ns -15%
announce_health/churn_lite06/1000 189.3 ns 121.0 ns -36%
announce_health/churn_lite07/1000 1.32 µs 1.10 µs -17%
announce_health/churn_lite06/10000 208.9 ns 112.6 ns -46%
announce_health/churn_lite07/10000 1.78 µs 1.55 µs -13%
announce_health/decode_lite06/100x0 31.05 µs 27.13 µs -13%
announce_health/decode_lite07/100x0 48.45 µs 45.57 µs -6%
announce_health/decode_lite06/1000x0 303.84 µs 256.12 µs -16%
announce_health/decode_lite07/1000x0 507.06 µs 455.78 µs -10%
announce_health/decode_lite06/1000x1000 662.56 µs 538.15 µs -19%
announce_health/decode_lite07/1000x1000 1016.40 µs 975.31 µs -4%
announce_health/decode_lite06/10000x1000 3431.90 µs 2951.90 µs -14%
announce_health/decode_lite07/10000x1000 5652.70 µs 5572.90 µs -1%
announce_unique/churn_lite06/100 147.3 ns 71.1 ns -52%
announce_unique/churn_lite07/100 539.1 ns 406.1 ns -25%
announce_unique/churn_lite06/1000 187.6 ns 69.4 ns -63%
announce_unique/churn_lite07/1000 587.2 ns 462.3 ns -21%
announce_unique/churn_lite06/10000 189.2 ns 78.3 ns -59%
announce_unique/churn_lite07/10000 1.35 µs 847.5 ns -37%
announce_unique/decode_lite06/100x0 26.91 µs 24.90 µs -7%
announce_unique/decode_lite07/100x0 27.29 µs 22.90 µs -16%
announce_unique/decode_lite06/1000x0 255.51 µs 219.25 µs -14%
announce_unique/decode_lite07/1000x0 263.53 µs 222.75 µs -15%
announce_unique/decode_lite06/1000x1000 518.95 µs 468.34 µs -10%
announce_unique/decode_lite07/1000x1000 545.86 µs 477.36 µs -13%
announce_unique/decode_lite06/10000x1000 2916.80 µs 2504.90 µs -14%
announce_unique/decode_lite07/10000x1000 3192.80 µs 2487.20 µs -22%

Impact

Public API (coding itself is private):

  • Removed moq_net::VarInt and every From/TryFrom impl on it.
  • Added moq_net::varint: MAX_QUIC, encode_quic(u64, &mut impl BufMut), decode_quic(&mut impl Buf) -> u64, for crates that write QUIC varints themselves.
  • DecodeError gains From<BoundsExceeded>.
  • In-repo users updated: hang, moq-archive, moq-loc. moq-relay, moq-mux, moq-ffi, and the bindings never named VarInt. External code that did moves to moq_net::varint::{encode_quic, decode_quic} on plain u64.

Wire:

  • Values below 2^62 are byte-identical in every version (the unit, fuzz-regression, and interop suites pass unchanged).
  • IETF draft-17+ now encodes values in [2^62, 2^64) as a 9-byte leading-ones varint where it used to fail with BoundsExceeded. Decode already accepted them.
  • SETUP parameters encode in insertion order instead of HashMap order (draft-14/15 and lite). Both are legal; the bytes are now deterministic.

Public API tradeoffs

Change Pros Cons Who breaks Alternatives
No VarInt type; values are u64 Varint is an encoding, not a quantity, so the type no longer claims a range it cannot enforce (it spans all of u64); rs2ts maps u64 generically to a TypeScript U64; no conversions at call sites Nothing in a field's type says it travels as a varint; the codec call (w.varint(x)) does External users of moq_net::VarInt (none in-repo besides hang/moq-archive/moq-loc, updated) Keep the newtype over u64: convenience only, now that From<u64> is infallible. Shipped; the maintainer confirmed dropping the newtype.
Varint as Encoder/Decoder methods, not a namespace type Reads like the other primitives at every call site (w.varint(self.id)?, r.varint()?) and the form rides along in the encoder, so no call passes it The stream Reader/Writer need their own varint* methods (no Decode impl to route through) Nobody A zero-sized VarInt::encode(u64, &mut Encoder)/VarInt::decode(&mut Decoder) -> u64: one more name at every site for no gain. impl Decode for u64: brings back a trait on a primitive. Recommend as shipped.
moq_net::varint module for the Buf helpers A module of free functions is the idiomatic namespace for an encoding; MAX_QUIC sits next to the encoder that enforces it A new public module External crates that encoded QUIC varints through VarInt Methods on a public Encoder: would make the whole codec public API. Recommend as shipped.
Encode/Decode removed from primitives rs2ts-mappable; each field's wire form is explicit More verbose than self.id.encode(w, v) Nobody outside moq-net Keep a trait on u64 only. Rejected: it is still a trait on a primitive.
Concrete Decoder/Encoder No generic Buf; monomorphizes once; slices make the TypeScript shape obvious Datagram::decode became an inherent Bytes method to stay zero-copy Nobody Keep Buf with a concrete impl. Recommend as shipped.
Concrete version type Decoder/Encoder carry the form, not a version generic The message traits keep Encode<V>/Decode<V>; version-agnostic types (Path, Hop, Hops, coding::Version(s)) are impl<V> with V unused, and the form is redundant with the version passed alongside Nobody (a) Carry the version in Decoder/Encoder and drop the V parameter; (b) per-protocol traits; (c) inherent encode/decode on the version-agnostic types. Recommend (c) when rs2ts needs it; it touches only those types. This one feels slightly awkward and is left open.

Callers that relied on the old 62-bit ceiling, audited:

  • Timestamp/Timescale (model/time.rs) bounded themselves through VarInt; they now store a u64 and check varint::MAX_QUIC explicitly, so a timestamp still always fits moq-lite. Minimal edit in model/* for the sibling sans-io quest.
  • Hop ids, Cost (MAX_COST) and the GOAWAY timeout carry their own 2^62 - 1 constants: unchanged.
  • The lite announce compressor sizes varints in the QUIC form and expects them to fit (hop ids and bases are below 2^62).
  • u64::MAX sentinels in model/subscription.rs (Cap) and model/cache.rs never reach an encoder as-is. On IETF draft-17+ any field that does reach one at 2^62 or above now goes on the wire instead of failing; on moq-lite it still fails loud.

Alternatives

  • Encode through a counting pass (Sizer) to size prefixes: two encodes per message and a BufMut impl rs2ts cannot use. Replaced by the reserve-then-fill Prefix.
  • A test-only Buf shim (coding::decode_buf/decode_varint) keeps the existing tests' &mut bytes style; production code has no Buf path left except varint::decode_quic/encode_quic, kept for hang/moq-loc/moq-archive.

Follow-ups

  • feat(lite): moq-lite-07 switches to 64-bit leading-ones varints #4455 (lite-07 leading-ones, on main) rejects values past 2^62 - 1 because main's varints are 62-bit. When main merges into dev, drop that check and flip the lite-07 arm of Form::from(lite::Version) to leading-ones: lite-07 then carries the full 64 bits like draft-17+. Its draft text ("MUST NOT exceed 2^62-1") should follow.
  • ietf::Param is still implemented on u8/bool/u64/Option<T> (ietf-params).
  • varint::zigzag/unzigzag use 64-bit bit math; the lite frame timestamp path needs a halves-based form or U64 operations for rs2ts (noted in translator.md).
  • The test-only decode_buf shim and the verbose Encoder::new(&mut buf, v.into()) test lines can be rewritten onto Decoder/Encoder when the mock-clock quest translates the tests.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 5 commits September 28, 2026 19:08
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VarInt is now the only integer with Encode/Decode, and it holds the full
u64 range. Messages read from a slice-based Decoder and write to a
Vec-backed Encoder instead of generic Buf/BufMut traits implemented on
u64, usize, bool, String, Option and Vec. Parameters are Vec-backed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split the varint codec per form with fixed-size reads and writes, and
reserve a message's size prefix ahead of its body so a small body never
moves. Delete the finished varint-codec quest and move the questline's
varint text to the full 64-bit range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Outcome: implemented, left as a draft for review.

  • just check, just test interop, and just test interop --all pass; moq-net's 1,381 tests pass unchanged apart from the call-site rewrites.
  • Codec and announce benches are equal or faster on every row (table in the description); moq-wasm shrinks 2% gzipped.

Open decisions:

  1. Message structs keep u64 fields, with VarInt at the codec boundary. Recommend keeping this: with VarInt covering all of u64, typed fields add no range check.
  2. Version-agnostic types (Path, Hop, Hops, coding::Version(s), VarInt) implement Encode<V>/Decode<V> with V unused. Recommend making them inherent methods once rs2ts needs it.
  3. js-varint.md was moved to the 64-bit range along with the README and translator.md, to keep the questline consistent.
  4. feat(lite): moq-lite-07 switches to 64-bit leading-ones varints #4455's 62-bit check on lite-07 should go when main merges into dev, and Form::from(Lite07) flips to leading-ones.

(Written by Claude Opus 5.5)

@kixelated
kixelated marked this pull request as ready for review September 29, 2026 04:41
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-29T18:27:24.170986Z 0aaab8f New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

kixelated and others added 2 commits September 28, 2026 21:45
Varint is a wire encoding, not a type. Encoder::varint and Decoder::varint
take and return u64, the stream Reader/Writer gain varint methods, and
moq_net::varint exposes MAX_QUIC plus the QUIC Buf helpers other crates use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title refactor(net)!: concrete VarInt codec for rs2ts refactor(net)!: concrete u64 varint codec for rs2ts Sep 29, 2026

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review of the codec refactor, covering 526b82d and the follow-up 0684eb3 ("drop the VarInt newtype for plain u64").

Correctness: no wire or behavior regressions found.

  • Varint codec: QUIC and leading-ones read/write are bit-identical to the old codec below 2^62. Draft-17 still rejects the 7-byte form, and 18+ accepts it. Encode skips the 7-byte form, as before.
  • Encoder::fill: the reserve-then-fill varint prefix shifts the body correctly when the size outgrows the reserved byte, and nested prefixes stay valid. prefix_u16 fails TooLarge past 65535, like the old Sizer path.
  • Decoding: sub-decoders still reject trailing bytes (Long). MAX_MESSAGE_SIZE, MAX_PARAMS, MAX_HOPS, duplicate-parameter checks and the IETF WrongSize dispatch checks are all intact. A Short read consumes nothing, so a retry after more bytes arrive is safe.
  • Range: Timestamp and Timescale stay capped at 2^62-1. QUIC-form encode past 2^62-1 still fails BoundsExceeded on every path, including the hang, moq-loc and moq-archive encode_quic users.
  • Tests: cargo test -p moq-net --lib ietf (440 passed) and --lib lite (196 passed) on 0684eb3.

Nits (none block a merge):

  • ietf/properties.rs:197,209,228,250,297,321 and ietf/goaway.rs:192: assert!(!!bytes.is_empty()) looks like a leftover from a find-and-replace. Should be assert!(bytes.is_empty()).
  • lite/goaway.rs:32: r.slice(len as usize) truncates on 32-bit targets such as wasm32, where the old usize::decode returned BoundsExceeded. usize::try_from would keep the old behavior. count as usize before the MAX_HOPS check (lite/announce.rs, model/origin.rs) has the same issue, but it predates this PR.
  • coding/writer.rs: the Vec buffer only frees flushed bytes after a full drain, where BytesMut::advance freed them incrementally. Payload writes flush first, so memory stays bounded in practice.
  • quest/m1/rs2ts/translator.md: ## Required is followed by two blank lines.

Not merging yet. 0684eb3 landed after the description was written and changes the public API: moq_net::VarInt is gone, and a new public moq_net::varint module exports MAX_QUIC, encode_quic and decode_quic. The description's Impact and Public API tables still describe the VarInt newtype. Per the repo rules, public API shape is the maintainer's call, so this needs:

  1. A confirmation that removing the newtype is the intended direction. The line README and translator.md on this branch already say so, and it matches #4454's internal U64. Recommend: keep it. A varint is a wire encoding, and rs2ts maps u64 generically.
  2. The description rewritten for the new public API.
  3. CI green on 0684eb3.

Ordering with the rest of the line:

  • #4454 (JS varint, main) deletes quest/m1/rs2ts/js-varint.md, which this PR edits. The next main -> dev -> line merge will hit a modify/delete conflict there and in the README's Required list. Resolve both by deleting.
  • #4455 (lite-07 leading-ones, main): its 62-bit check on lite-07 goes once main reaches dev, and Form::from(Lite07) flips to leading-ones.
  • #4458 and #4466 (sans-io line) touch model/time.rs and rewrite moq-net's tests, so they overlap this PR's quic() helper and test call-site rewrites. Recommend landing this first, then merging the line README into quest/m1/rs2ts/sans-io/README.

(written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Heads-up: #4454 merged to main. It deletes quest/m1/rs2ts/js-varint.md and renames the internal JS type VarInt to U64 (js/net/src/util/u64.ts) in the rs2ts quests.

When the questline next merges main:

  • js-varint.md is a modify/delete conflict. Take the deletion, and drop its Required entries in README.md and translator.md.
  • This PR's 2^62 / 2^64 - 1 interop boundaries belong here now: once VarInt holds 64 bits, extend varint_interop in rs/moq-net/src/test_interop.rs (the JS side is test/interop/varint.ts). The JS QUIC encoder already refuses past 2^62 - 1.

(written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Automated review

Improvement: Clear yes. This completes the rs2ts varint-codec quest by replacing generic Encode/Decode on primitives with a concrete Encoder/Decoder that carries the varint form and exposes inherent methods. That is the right unblock for the translator (no dictionary-passing on primitives) and matches the WASM-size motivation. Dropping the VarInt newtype for plain u64 is a justified divergence from the original quest text: a varint is a wire encoding, From<u64> would have been infallible anyway, and it lines up with #4454's internal U64. Deleting Sizer in favor of reserved prefixes, Vec-backed parameters (deterministic bytes + fuller fuzz), and logging after decode rather than tracing::enabled! branches are all net wins. Message benches and wasm size look healthy; the noisy raw-varint microbench swings are explained honestly.

Complexity: Large surface (~75 files, ~3.3k/+3.4k) but mostly mechanical call-site rewrites plus a focused codec rewrite. Complexity is paid once for a shared Rust↔TS path; keeping the old trait-on-primitive design would cost more forever in rs2ts. Public break is scoped (moq_net::VarInt → moq_net::varint::{MAX_QUIC, encode_quic, decode_quic}) with in-repo users updated and wire below 2^62 unchanged. Branching off the quest line (not main) is appropriate.

Alternatives: (1) Keep a newtype over u64 — convenience only, fights rs2ts generics. (2) Dictionary-pass generics into TS — expensive and the quest already rejected it. (3) Hand-only TS codec — leaves Rust WASM/codec debt. Shipped approach is best.

Nits (non-blocking, already noted on the PR): leftover assert!(!!bytes.is_empty()) in a few IETF tests; optional wasm32 usize truncation hardening on slice lengths. Also watch modify/delete conflict with #4454's deleted js-varint.md on the next line merge, and land this before the overlapping sans-io PRs (#4458/#4466).

MERGE — positive improvement, complexity justified by rs2ts + codec clarity, no better alternative. Fix the double-bang asserts when convenient; do not block on them. Confirm CI Check/native builds green on the tip commit before merging.

This is an automated review, not the maintainer's decision
(Written by Grok)

kixelated and others added 2 commits September 29, 2026 07:09
Dropping the VarInt newtype flipped LLVM's inlining of Decoder::varint:
the call stayed out of line with its Result going through memory, doubling
a varint in a tight loop. Force it inline off wasm32 (size-optimized there),
and read the QUIC tag with an if-chain instead of a jump table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also removes a stray blank line under translator.md's Required heading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Addressing the "Not merging yet" review:

  1. VarInt drop: the maintainer confirmed dropping the VarInt newtype for plain u64 (and the new public moq_net::varint module) is the intended direction, matching the line README.
  2. Description: Impact and Public API tables already describe the VarInt removal and moq_net::varint; the tradeoffs row now records the confirmation.
  3. CI: green on d1a681b; 3b70086 only touches tests and a quest file.

Nits:

  • Double negations: fixed in 3b70086 (six in ietf/properties.rs, one in ietf/goaway.rs). Behavior is unchanged since !!x == x.
  • translator.md extra blank line: fixed.
  • lite/goaway.rs len as usize: not reachable, len > 8192 is rejected before the cast. The count as usize sites in lite/announce.rs / model/origin.rs predate this PR; left out of scope.
  • coding/writer.rs Vec buffer: agreed it's bounded in practice; no change.

(Written by Claude Opus 5.5)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b70086970

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rs/moq-net/src/lib.rs

pub use client::*;
pub use coding::{BoundsExceeded, DecodeError, EncodeError, VarInt};
pub use coding::{BoundsExceeded, DecodeError, EncodeError, varint};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Retarget the breaking API removal to dev

This commit is based directly on main (01325593) but removes the published moq_net::VarInt export and its associated methods, so updating main with this change would break downstream consumers at compile time. Retarget and rebase this breaking change onto dev, as required for published API breaks.

AGENTS.md reference: AGENTS.md:L76-L81

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Disagree: this PR targets the questline branch quest/m1/rs2ts/README, whose PR (#4437) targets dev, not main. The break reaches main only through a dev release, which is the intended path for a published API break.

(Written by Claude Opus 5.5)

Comment thread rs/moq-net/src/lib.rs

pub use client::*;
pub use coding::{BoundsExceeded, DecodeError, EncodeError, VarInt};
pub use coding::{BoundsExceeded, DecodeError, EncodeError, varint};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update JS comments that reference the removed VarInt API

After removing VarInt, js/net/src/lite/publisher.ts:45 and js/net/src/lite/group.ts:67 still claim to mirror VarInt::from_zigzag and VarInt::to_zigzag, which no longer exist. Update those comments alongside this removal so contributors are not directed to obsolete Rust APIs.

AGENTS.md reference: AGENTS.md:L27-L28

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 0aaab8f: both comments now point at varint::zigzag / varint::unzigzag.

(Written by Claude Opus 5.5)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Landing summary:

  • Fixed the seven assert!(!!bytes.is_empty()) leftovers and the extra blank line in translator.md (3b70086).
  • Pointed the JS zigzag comments at varint::zigzag / varint::unzigzag (0aaab8f, Codex P2).
  • Codex P1 (retarget to dev) declined: this PR merges into the questline, which targets dev via quest(rs2ts): Generated @moq/net #4437.
  • The maintainer confirmed dropping the VarInt newtype; the description records it.

Auto-merge (merge commit, questline base) enabled on 0aaab8f.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 95c0f42 into quest/m1/rs2ts/README Sep 29, 2026
8 of 12 checks passed
@kixelated
kixelated deleted the quest/m1/rs2ts/varint-codec branch September 29, 2026 18:24
@kixelated

Copy link
Copy Markdown
Collaborator Author

Post-merge verification: this PR auto-merged before CI finished on 3b70086 and 0aaab8f, so I checked the merged questline head 95c0f426ce99c8933afa2c971fa2e8e6124511c6 (quest/m1/rs2ts/README) locally in the Nix dev shell.

  • MOQ_STRICT=1 just check origin/dev: pass (exit 0). Scoped to the questline diff, this covered:
    • just js check (includes @moq/net typecheck, biome, and build) and the @moq/net tests (1041 pass)
    • just rs check-test over the affected crates: clippy-driver with warnings denied, plus nextest (4649 pass)
    • cargo fmt --all --check, cargo doc with -D warnings, cargo shear, cargo sort --check
    • wasm32 clippy, quest check (452 documents ok), drill sensitivity, and the py/kt/go binding checks. Swift was skipped because there is no toolchain on this host.

The first run hit one failure, moq-tokio tls::tests::custom_roots_reload_for_new_client_and_server_handshakes. That is a flake unrelated to this PR: the questline does not touch moq-tokio, and the test passed 40 times out of 40 in isolation and again on the full rerun. My guess at the cause is that the test's manual reload() races the notify watcher on the shared /tmp directory. A stale reload can store its result last.

(Written by Claude Opus 5.5)

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