Skip to content

refactor(quic)!: implement native WebTransport poll transports - #7771

Merged
kixelated merged 21 commits into
mainfrom
quest/m1/quic-api/native-web
Nov 4, 2026
Merged

kixelated merged 21 commits into
mainfrom
quest/m1/quic-api/native-web

Conversation

@kixelated

@kixelated kixelated commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Native QUIC sessions passed through web_transport_trait adapters that boxed operations, copied writes/datagrams, and emulated terminal waits. The driver-poll prerequisite (moq-dev/moq#7707) is merged; those adapters still remained. io_uring also rescanned every partial stream header whenever any stream progressed.

Approach

  • Implement moq-net's poll traits directly on both native WebTransport stacks, keeping driver send groups, full priorities, borrowed writes, leases, metrics, receive windows, and request admission. Delete the generic adapters, sealed grouping traits, poll_op, and OpSlot.
  • Align stream and handshake APIs around send::Stream, recv::Stream, consuming terminal methods, send::Finished, protocol CONNECT types, and explicit connection() access. Document quic::web as a supported library in both crates.
  • Chain datagram prefixes with their payload. Classify io_uring headers with wake-driven FuturesUnordered; partial/application streams retain their stream credit, and three role slots retain HTTP/3 critical streams.
  • Observe clean receive FIN without consuming payload, drain classified uni/bi headers before connection failure, and migrate relay io_uring callers directly to moq-net.
  • Return freed QUIC stream credit when the peer has spent every advertised stream. The repeated partial-header sweep exposed the existing batch-threshold deadlock; this preserves the stream-credit bound instead of loosening the fixture or the limit.

Impact

  • moq-tokio, breaking: pass quic::web::Session directly to moq-net. Remove generic transport::Session<S>/send/receive/error adapters, Grouped/GroupedSend, and all web_transport_trait implementations. Custom transports implement moq_net::transport::poll directly.
  • moq-uring, breaking: replace Local::new(transport::Session::new(session, address)) with Local::new(session). The native session directly implements moq-net's poll traits; the generic adapters and web_transport_trait implementations are removed.
  • Both native libraries, breaking: use role-local stream/error names; finish(self) returns send::Finished, while reset(self, code)/stop(self, code) consume their stream. Session polls take &self, &kio::Waiter, and driver send options. Stream IDs use id(). Datagrams accept impl Buf. stats() exposes the driver snapshot; use moq-net metrics() for transport metrics.
  • Handshake API, breaking: access request fields through proto::ConnectRequest; use connection(), proto::ConnectResponse, and http::StatusCode. respond requires OK; reject requires 4xx/5xx. Tokio removes Session's Deref, the web::Server helper, and Request::connect(). io_uring removes Response/Rejected. Both crates have migration notes under Unreleased.
  • Behavior: empty FIN completes closed() without a read; buffered payload, including leased bytes handed back unread, must be read first. A datagram that names no session or another session is dropped rather than failing the session (Tokio failed it before this PR as well). poll_reset remains a failure-only wait. Header-write stops preserve valid application codes, and invalid stream codes remain typed failures. Raw application error codes outside u32 are not advertised as a valid moq-net registry code.
  • moq-relay: direct native io_uring transport; no public relay API or configuration change. All three crates drop web-transport-trait; qmux retains its independent interop dev-dependency.
  • moq-quic: return MAX_STREAMS below the batching threshold once all advertised stream IDs are used, preventing held partial headers from freezing unrelated stream turnover. A read no longer reports the end of a finished stream while leased bytes are unread. No public API change.
  • Wire format: unchanged, including raw QUIC, WebTransport framing/error mapping, and MoQ versions. MAX_STREAMS can be sent sooner. No package version bumps.

Alternatives

Keeping the generic adapters would retain the boxing/copies after the drivers gained native polls. A cap on incomplete headers only refuses peers; retaining stream credit bounds their storage by negotiated flow control. Re-polling a vector still costs a scan on every ready stream, so io_uring now queues only new/woken headers.

The stream-credit fix also applies to both drivers and QMux through the shared engine. Upstream retains the same batch-only condition; it was checked before fixing the local source.

The aligned API covers the operations both runtimes already support. Tokio retains its existing client CONNECT helper; io_uring's existing WebTransport handshake remains server-side. Runtime-specific connection controls remain accessible through connection().

Measurements

Base: d1bcd5748b5565abae7b8d95885dd4b3cf04cbe7 (driver polls merged). Native implementation measured at 20e94509dd9a2507614a25d82b96751b9a35abec, including the stream-credit fix. The later review fixes validate local rejection statuses and map header-write failures; neither path runs in these successful-session workloads.

Rust 1.98.1 in the pinned Nix shell, Linux x86_64, optimized profiling profile. Both revisions use the same workload and current generator. Relay profiles pin CPUs 0-7 and use one runtime worker. Available SSH hosts were busy or unavailable, and this host had concurrent builds, so these are diagnostic pairs, not a CPU-capacity or statistically isolated improvement claim.

Relay: just bench-profile heap fanout 1 <tokio-workers|io-uring-workers> d1bcd5748b5565abae7b8d95885dd4b3cf04cbe7. One publisher, 64 subscribers, 10 fps, 400-byte frames, one frame per group stream. Every run delivered its offered load with zero reported loss. CPU is measured in the unprofiled control and normalized by delivered application payload; allocation estimates come from the separate sampled jemalloc steady window and include all relay work, divided by delivered streams.

Runtime Revision Delivered frames/s CPU cores/Gbps Estimated relay allocations/stream Heap churn MiB/s
tokio-workers base 640.0 20.47 64.96 11.97
tokio-workers current 639.9 17.40 22.05 7.66
io-uring-workers base 640.0 16.58 28.81 7.61
io-uring-workers current 640.0 16.57 30.32 7.54

Tokio's paired relay allocation estimate and churn drop; io_uring's CPU/churn are roughly flat and its sampled allocation count is slightly higher. The heavier 256-subscriber, 60 fps/1200-byte diagnostic lost substantial Tokio traffic under concurrent host load, so it does not establish a paired performance improvement.

Native transport: identical fanout allocation-counter instrumentation on both revisions, with the base using its old adapter. NATIVE_ALLOCS=1 cargo bench --locked --profile profiling -p moq-tokio --bench fanout -- native_transport. A current-thread loopback runtime counts allocation/reallocation calls and requested bytes across both endpoints after 16 warmup batches, over 64 measured batches; setup and teardown are excluded. Payloads are verified byte for byte. These exact counters are a different scope from the relay estimates.

Payload bytes Streams/batch Base allocations/stream Native allocations/stream Base requested bytes/stream Native requested bytes/stream
64 1 21.61 13.59 8797 6354
64 16 19.22 11.24 4557 2128
64 256 18.53 10.53 4412 1972
65536 1 30.09 22.11 154552 86641
65536 16 45.14 37.16 278110 210139
65536 256 37.60 30.38 245974 165515

Header sweep: the permanent io_uring web_headers benchmark, cargo bench --locked --profile profiling -p moq-uring --bench web_headers -- --quick, completed all combinations. Indicative end-to-end time per batch (one uni and one bi per ready stream):

Pending partial headers per direction Ready=1 Ready=16
0 20.70 µs 94.17 µs
64 30.25 µs 93.19 µs
256 35.76 µs 96.16 µs

This includes network scheduling and credit updates, not only parser CPU. The repeated integration fixture checks every completed fragmented tail and FIN; the implementation polls only new/woken header futures. Both new timing series run in the existing nightly targets.

Checks

Iteration after the merge queue rejection, at 24e2220bae10a5bac602944d8c0f012e8c67fda9:

  • The queue run (Queue, run 41752982583) failed only moq-ffi origin::supervision_tests::a_panicked_origin_task_reaches_the_host_while_another_origin_keeps_working, which doesn't touch the QUIC transports. Main already tracks it, from this same run, as the flake quest/m0/test-flakes-2/ffi-origin-panic.md.
  • Merged origin/main twice (no rebase). The first merge brought in datagram tracks (moq-dev/moq#7752), MTU probes on validated paths (moq-dev/moq#7788), and Observation fields (moq-dev/moq#7776); the only textual conflicts were Cargo.lock and rs/moq-tokio/MIGRATE.md, and both migration entries are kept. The second brought in the ACME removal (moq-dev/moq#7803): moq-relay now drops both x509-parser (main) and web-transport-trait (this PR).
  • Fixed four review threads, each with a regression test. The tokio and io_uring tests failed before the fixes:
    • Tokio send reset reads the driver-recorded stop again, so a STOP_SENDING racing the reset still reports its code. The window lies between two lock acquisitions, so it can't be tested deterministically; the driver's stop-before/after-reset tests cover the outcome.
    • Tokio's receive close watch returns a cached reset (a_read_reset_stays_reset_for_the_close_watch).
    • moq-quic doesn't report FIN past unread leased bytes (unread_lease_holds_the_end_off, plus an_unread_lease_holds_the_close_watch_off in tokio and io_uring).
    • Both stacks drop stray datagrams (a_stray_datagram_is_dropped in tokio and io_uring).
  • just rs lint, cargo clippy --workspace --all-targets --all-features -D warnings, and cargo nextest run --all-features -p moq-quic -p moq-tokio -p moq-uring -p moq-relay -p moq-native -p moq-net -p qmux@0.7.0 -p moq-cli all pass: 5,688 passed, 82 skipped.
  • just test interop --tail: all checks passed, on the first merge head.

Earlier rounds:

  • On 5d47c8556c82a61f707ef3b84cac8ad122804c23, nix develop --command quest gate just ci: Rust lint/rustdoc passed; all 11,622 selected Rust tests passed (132 configured skips). Repository formatting, markdown, shell, TOML/Nix, bench checks, drift checks, quest validation and GitHub static checks passed.
  • Strict CI exited 1 for three base/session failures: runner-image unshare: write failed /proc/self/uid_map: Operation not permitted; Bun's moq-doc script cannot find tsc; the direnv hook regression test loses its mock PATH. just gate flake, the same just js check-changed <doc-list> selection, and bash .claude/hooks/direnv.test.sh reproduce them in the unchanged d1bcd5748b checkout. The latter two use the same Codex snapshot as this checkout. No harness or environment setting was disabled to get a pass.
  • quest check --base origin/main: 563 documents OK after quest deletion, dependent prose repairs, and sizing the childless API audit's remaining work. Final markdown check and git diff --check pass.
  • Review fixes: both invalid-rejection tests and the header stop-code assertion failed before the fix. Both runtimes now refuse non-4xx/5xx statuses before writing; the new cases and the existing 404-delivery case all pass.
  • The original empty-FIN close watch timed out on the driver-poll base. Both stacks now cover clean FIN; Tokio also drains buffered uni/bi headers before terminal connection failure. Existing receive-credit, send-group, request-admission, reset/stop and relay io_uring suites pass.
  • The repeated header sweep initially stalled after three ready streams at pending=64, ready=1. The shared engine's batch threshold withheld all returned stream IDs. The engine test and repeated 16-batch integration sweep pass after the source fix. The permanent header benchmark now completes every combination.
  • just check origin/main was also attempted: its ordinary flake probe failed while importing an untracked cache-agent.sock before reaching the user-namespace probe. The strict flake module above reproduces the actual runner-image failure on the unchanged base. Recommend making the ordinary probe use a tracked Git source in the CI follow-up.
  • Local git merge-tree --write-tree origin/main HEAD: clean.
  • No MoQ wire format, FFI, gateway, or CLI interface changed; all-language interop was not added to this transport-only check selection.

Review

Claude Code (quest review --harness claude), Claude Opus 5.5, medium effort, session b3360aa6-0083-4c6f-a926-993d45ed50b3. Most of the code carries GPT-6.1 Sol trailers, so Claude reviews.

  • Round 1 at 32e6224677bbd621bfbb24010a5f75a9feef3564 found two issues:
    • Shared tokio/io_uring send/recv wrappers (medium): refuted. The two reset_mut shapes differ because tokio's driver runs on another thread behind a lock, while io_uring's is a single-threaded RefCell; io_uring now has a comment saying so. The tokio expect is the pre-PR invariant (base rs/moq-tokio/src/quic/web/send.rs:108). The concrete drift it found, io_uring's poll_reset doc, is fixed. This repeats the extraction the earlier session refuted.
    • Cancelled open parks in a session slot (low): refuted. moq-net's poll_open_*(&self, waiter, options) has no per-call state, and the pre-PR adapter parked the same in-flight open in a session-wide OpSlot (base rs/moq-tokio/src/transport/owned.rs:91-112). Each kind holds at most one, and the next open of that kind adopts it with its own options.
  • Round 2 at 01cf8b10faf44830d81f388715d96b1cbc744d57: 0 findings.
  • Head b5c37c2d2abe118ea0e99934ce99bd5e639ec29a adds only merges of origin/main. They resolve Cargo.lock and rs/moq-relay/Cargo.toml (the ACME removal), then quest/m2/net-wrap/README.md: main's runtime-seam link is kept, and this PR's transport-seam line drops the deleted native-web.md link. git diff origin/main...HEAD adds and removes exactly the lines that round 2's head did against its base; only diff context differs. That is the guide's hand-resolved-merge exemption. On b5c37c2d2a, just rs lint and workspace clippy pass, and 5,699 tests pass across the QUIC and dependent crates (82 skipped).

The earlier session (2085d17a, rounds 1-2 at 5d47c855) refuted the wrapper extraction and accepted the rejection-status guards.

Follow-ups

  • Existing relay allocation quest owns broader retained-heap profiling.
  • The strict runner-image failure is already explained in moq-dev/moq#7763, with independent gate failures in moq-dev/moq#7756 and moq-dev/moq#7759. Ordinary probe/skip and strict failure policy are preserved.
  • The session snapshot PATH problem is already proposed as quest/m1/codex-env-snapshot.md in moq-dev/moq#7747. It also replaces the mocked PATH in the direnv hook test. Recommend repairing snapshot composition in that work, without changing this transport PR's check harness.
  • Suggested follow-up quest: offer the held-stream credit fix and deterministic test upstream.
  • Quiet-host profiling is already planned by the benchmark host-lock work in moq-dev/moq#7748; retake CPU comparisons there before claiming a capacity improvement.

(Written by Claude Opus 5.5)

kixelated and others added 15 commits November 3, 2026 14:47
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Apply the local reviewer’s comment move unchanged.

Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
@kixelated kixelated changed the title Native WebTransport implements moq-net's traits refactor(quic)!: implement native WebTransport poll transports Nov 4, 2026
@kixelated
kixelated marked this pull request as ready for review November 4, 2026 00:29
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 27 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 20561324-cf5b-4f3a-b962-94f9fd7b911c
Commits

Reviewing files that changed from the base of the PR and between d9a140c and b5c37c2.

Files ignored due to path filters: 1
  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 67
  • bench/README.md
  • doc/concept/transport.md
  • quest/m1/quic-api/README.md
  • quest/m1/quic-api/native-web.md
  • quest/m2/net-wrap/README.md
  • quest/m2/perf/quic-lock-contention.md
  • quest/m2/perf/relay-allocs.md
  • quest/m2/quic-sansio-driver.md
  • rs/moq-quic/README.md
  • rs/moq-quic/src/connection/streams/lease.rs
  • rs/moq-quic/src/connection/streams/recv.rs
  • rs/moq-quic/src/connection/streams/state.rs
  • rs/moq-relay/Cargo.toml
  • rs/moq-relay/src/uring.rs
  • rs/moq-relay/tests/integration/refusals.rs
  • rs/moq-relay/tests/integration/runtime_uring.rs
  • rs/moq-tokio/Cargo.toml
  • rs/moq-tokio/MIGRATE.md
  • rs/moq-tokio/README.md
  • rs/moq-tokio/benches/fanout.rs
  • rs/moq-tokio/src/client.rs
  • rs/moq-tokio/src/quic.rs
  • rs/moq-tokio/src/quic/backend.rs
  • rs/moq-tokio/src/quic/driver/recv.rs
  • rs/moq-tokio/src/quic/driver/send.rs
  • rs/moq-tokio/src/quic/web/connect.rs
  • rs/moq-tokio/src/quic/web/error.rs
  • rs/moq-tokio/src/quic/web/mod.rs
  • rs/moq-tokio/src/quic/web/recv.rs
  • rs/moq-tokio/src/quic/web/send.rs
  • rs/moq-tokio/src/quic/web/server.rs
  • rs/moq-tokio/src/quic/web/session.rs
  • rs/moq-tokio/src/quic/web/settings.rs
  • rs/moq-tokio/src/server.rs
  • rs/moq-tokio/src/transport.rs
  • rs/moq-tokio/src/transport/grouped.rs
  • rs/moq-tokio/src/transport/owned.rs
  • rs/moq-tokio/tests/integration/datagram_delivery.rs
  • rs/moq-tokio/tests/integration/receive_credit.rs
  • rs/moq-tokio/tests/integration/sim.rs
  • rs/moq-uring/Cargo.toml
  • rs/moq-uring/MIGRATE.md
  • rs/moq-uring/README.md
  • rs/moq-uring/benches/echo_quic.rs
  • rs/moq-uring/benches/session_lite.rs
  • rs/moq-uring/benches/web_headers.rs
  • rs/moq-uring/src/lib.rs
  • rs/moq-uring/src/quic/client.rs
  • rs/moq-uring/src/quic/driver/connection.rs
  • rs/moq-uring/src/quic/driver/recv.rs
  • rs/moq-uring/src/quic/driver/send.rs
  • rs/moq-uring/src/quic/mod.rs
  • rs/moq-uring/src/quic/web.rs
  • rs/moq-uring/src/quic/web/recv.rs
  • rs/moq-uring/src/quic/web/send.rs
  • rs/moq-uring/src/transport.rs
  • rs/moq-uring/src/transport/adapter.rs
  • rs/moq-uring/src/transport/local.rs
  • rs/moq-uring/tests/integration/echo.rs
  • rs/moq-uring/tests/integration/endpoint.rs
  • rs/moq-uring/tests/integration/main.rs
  • rs/moq-uring/tests/integration/receive_credit.rs
  • rs/moq-uring/tests/integration/session.rs
  • rs/moq-uring/tests/integration/transport.rs
  • rs/moq-uring/tests/integration/web.rs
  • rs/moq-uring/tests/integration/web_headers.rs
  • sh/gh/nightly.sh
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@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.

Automated review by review (OpenAI)

Reviewed head fd5d25e.

  1. [P2] Keep the close probe from discarding unread leases — rs/moq-tokio/src/quic/driver/recv.rs:235–241. A lease moves bytes out of the assembler but retains unread bytes separately. If a payload and FIN are leased and the lease is dropped unread, this ordinary zero-byte Chunks read sees the assembler at FIN and frees the stream, discarding the retained payload. closed() returns success although data is unread, and a later lease on the live connection can panic. Observe FIN without a plain read that frees a leased stream; add a payload+FIN, dropped-unread-lease, close, re-lease regression.

  2. [P2] Release canceled partial opens — rs/moq-tokio/src/quic/web/session.rs:314–336. The pending send/recv pair lives in a session-wide slot rather than the open_bi future. Cancel an open while its WebTransport header is blocked by flow control: the pair still holds a bidi stream credit. With one credit, poll_open_request uses a different slot and keeps returning None even after the pressure clears, until another ordinary open polls the abandoned pair or the session drops. The previous direct async open dropped its local streams on cancellation. Tie the partial open to its operation or arrange cancellation cleanup, and test this cross-kind case.

  3. [P2] Preserve a cached reset in the close result — rs/moq-tokio/src/quic/driver/recv.rs:233–245. After a read observes RESET_STREAM, poll_read_generic sets both reset and all_data_read; the next closed() returns success at the all-data-read check before seeing the reset. Two close calls likewise produce error then success. Check the cached reset first, as the io_uring implementation does, and cover both orders.

Verification: static GitHub review of the two drivers, shared stream-credit logic, transport contracts and tests. I did not run tests, exercise cancellation or reproduce these cases.

@kixelated
kixelated added this pull request to the merge queue Nov 4, 2026

@pullfrog pullfrog 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.

Important

A few correctness issues in the new terminal stream paths, plus one uring datagram behavior change, are worth a look before merge.

Reviewed changes

  • Native poll implementations. moq-tokio and moq-uring quic::web sessions now implement moq_net::transport::poll directly; the generic adapters, Grouped/GroupedSend, poll_op, and the web-transport-trait dependency are gone.
  • Aligned stream and handshake API. Role-local send::Stream/recv::Stream, consuming finish/reset/stop, send::Finished, Request deref to proto::ConnectRequest, typed CONNECT responses, and connection() accessors.
  • io_uring header sweep. Partial headers move to FuturesUnordered, critical streams use three role slots, and the handshake stream cap is removed; freed stream credit is returned once the peer spends all advertised streams.
  • Stream-credit fix. queue_max_stream_id no longer withholds returned credit below the batch threshold when the peer has used every advertised stream.
  • Tests, benchmarks, docs, quests. web_headers nightly bench/test, native transport allocation bench, rejection and clean-FIN cases, migration notes, and documentation links.

The stream-credit condition is safe (advertised value matches enforcement and stays monotonic), and I could not find a lost wake or stream leak in the shared Opening/SessionAccept state. The findings below are in the new terminal paths.

ℹ️ Quest audit still names the deleted transport adapter

The dependent quest prose in net-wrap, quic-lock-contention, relay-allocs, and quic-sansio-driver was updated, but quest/m2/audit-compile-time.md:291-296 still measures moq_tokio::transport::Session<quic::web::Session>, a type this PR deletes. Annotate the historical measurement or point it at the native implementation so the audit does not describe code that no longer compiles.

Technical details
# Stale audit reference

## Affected sites
- `quest/m2/audit-compile-time.md:291-296` — transport-rows table still names
  `moq_tokio::transport::Session<quic::web::Session>` and
  `moq_tokio::transport::Session`.

## Required outcome
- Either note that the measurement predates #7771 and the adapter no longer
  exists, or replace the reference with the native `quic::web::Session`.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

Comment thread rs/moq-tokio/src/quic/web/send.rs Resolved
Comment thread rs/moq-tokio/src/quic/driver/recv.rs Resolved
Comment thread rs/moq-tokio/src/quic/driver/recv.rs Resolved
Comment thread rs/moq-uring/src/quic/web.rs Resolved
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 4, 2026
@moq-bot

moq-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

quest land watch: the merge queue removed this pull request at fd5d25e because its merge_group build failed (failed_checks).

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.

Queue / Queue (job) failure at step Check:

…documented under each public name [1.12ms]
moq-doc test: (pass) a rendered workspace > a namespace export keeps its public name [1.08ms]
moq-doc test: (pass) a rendered workspace > a hidden declaration is documented under the alias that re-exports it [0.97ms]
moq-doc test: �[93m[warning]�[0m Value, defined in @test/hidden/src/access.ts, is referenced by read but not included in the documentation
moq-doc test: �[93m[warning]�[0m Value, defined in @test/hidden/src/access.ts, is referenced by read but not included in the documentation
moq-doc test: (pass) an unexported declaration that a public one names fails the run [383.81ms]
moq-doc test: �[93m[warning]�[0m Failed to resolve link to "Missing" in comment for value
moq-doc test: (pass) a link that does not resolve fails the run [184.63ms]
moq-doc test: 
moq-doc test: ::endgroup::
moq-doc test: 
moq-doc test: ::group::concept/audio-jitter/corpus.test.ts:
moq-doc test: (pass) audio jitter corpus > paced > matches the checked-in file [0.31ms]
moq-doc test: (pass) audio jitter corpus > paced > is a trace of equal-length clocks [0.03ms]
moq-doc test: (pass) audio jitter corpus > paced > arrives in a monotonic order [0.93ms]
moq-doc test: (pass) audio jitter corpus > paced > names a target for the first frame [0.03ms]
moq-doc test: (pass) audio jitter corpus > paced > reports every target as a change [0.06ms]
moq-doc test: (pass) audio jitter corpus > flush > matches the checked-in file [0.03ms]
moq-doc test: (pass) audio jitter corpus > flush > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > flush > arrives in a monotonic order [0.22ms]
moq-doc test: (pass) audio jitter corpus > flush > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > flush > reports every target as a change
moq-doc test: (pass) audio jitter corpus > advertised > matches the checked-in file [0.03ms]
moq-doc test: (pass) audio jitter corpus > advertised > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > advertised > arrives in a monotonic order [0.21ms]
moq-doc test: (pass) audio jitter corpus > advertised > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > advertised > reports every target as a change
moq-doc test: (pass) audio jitter corpus > buildup > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > buildup > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > buildup > arrives in a monotonic order [0.25ms]
moq-doc test: (pass) audio jitter corpus > buildup > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > buildup > reports every target as a change [0.01ms]
moq-doc test: (pass) audio jitter corpus > reorder > matches the checked-in file [0.02ms]
moq-doc test: (pass) audio jitter corpus > reorder > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > reorder > arrives in a monotonic order [0.17ms]
moq-doc test: (pass) audio jitter corpus > reorder > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > reorder > reports every target as a change
moq-doc test: (pass) audio jitter corpus > backlog > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > backlog > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > backlog > arrives in a monotonic order [0.36ms]
moq-doc test: (pass) audio jitter corpus > backlog > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > backlog > reports every target as a change
moq-doc test: (pass) audio jitter corpus > idle > matches the checked-in file [0.03ms]
moq-doc test: (pass) audio jitter corpus > idle > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > idle > arrives in a monotonic order [0.18ms]
moq-doc test: (pass) audio jitter corpus > idle > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > idle > reports every target as a change
moq-doc test: (pass) audio jitter corpus > tunein > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > tunein > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > tunein > arrives in a monotonic order [0.32ms]
moq-doc test: (pass) audio jitter corpus > tunein > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > tunein > reports every target as a change
moq-doc test: (pass) audio jitter corpus > spike > matches the checked-in file [0.06ms]
moq-doc test: (pass) audio jitter corpus > spike > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > spike > arrives in a monotonic order [0.24ms]
moq-doc test: (pass) audio jitter corpus > spike > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > spike > reports every target as a change
moq-doc test: (pass) audio jitter corpus > straggler > matches the checked-in file [0.04ms]
moq-doc test: (pass) audio jitter corpus > straggler > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > straggler > arrives in a monotonic order [0.17ms]
moq-doc test: (pass) audio jitter corpus > straggler > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > straggler > reports every target as a change
moq-doc test: (pass) audio jitter corpus > fixed > matches the checked-in file [0.01ms]
moq-doc test: (pass) audio jitter corpus > fixed > is a trace of equal-length clocks
moq-doc test: (pass) audio jitter corpus > fixed > arrives in a monotonic order [0.07ms]
moq-doc test: (pass) audio jitter corpus > fixed > names a target for the first frame
moq-doc test: (pass) audio jitter corpus > fixed > reports every target as a change
moq-doc test: 
moq-doc test: ::endgroup::
moq-doc test: 
moq-doc test:  134 pass
moq-doc test:  0 fail
moq-doc test:  15964 expect() calls
moq-doc test: Ran 134 tests across 3 files. [3.41s]
moq-doc test: Exited with code 0
js: test finished in 27.70s
##[endgroup]
##[group]check markdown
[0.04ms] ".env"
bun install v1.4.2 (744846f84)
Resolving dependencies
Resolved, downloaded and extracted [1]

$ bun run --cwd js/libav build
$ bun build.ts
warning: ignoring untrusted flake configuration setting 'extra-substituters'.
Pass '--accept-flake-config' to trust it
warning: ignoring untrusted flake configuration setting 'extra-trusted-public-keys'.
Pass '--accept-flake-config' to trust it
warning: ignoring untrusted flake configuration setting 'fallback'.
Pass '--accept-flake-config' to trust it

Checked 771 installs across 848 packages (no changes) [258.00ms]
##[endgroup]
##[group]check gh
python3 sh/version.py
shared version: disabled until the release cut runs just version
actionlint
zizmor --offline --quiet .github
�[33m WARN�[0m �[2mzizmor::registry::input�[0m�[2m:�[0m failed to validate file://.github/workflows/dependabot.yml as dependabot config: input does not match expected validation schema
�[32mNo findings to report. Good job!�[39m (�[1m�[93m28�[39m ignored, �[93m114�[39m suppressed�[0m)
sh/gh/alert.sh check-coverage
alert.yml watches all 34 alertable workflows
bun test sh/gh/alert.test.ts
bun test v1.4.2 (744846f84)

##[group]sh/gh/alert.test.ts:
(pass) Discord alert policy for pull_request [0.20ms]
(pass) Discord alert policy for pull_request_target [0.12ms]
(pass) Discord alert policy for push [0.09ms]
(pass) Discord alert policy for schedule [0.10ms]
(pass) Discord alert policy for workflow_dispatch [0.04ms]
(pass) Discord alert policy for workflow_run [0.02ms]
(pass) cron workflows only alert on scheduled failures [8.38ms]
(pass) ordinary CI never subscribes to Discord alerts [0.13ms]
(pass) alert coverage excludes CI-only workflows and retains release and nightly workflows [39.57ms]

##[endgroup]

 9 pass
 0 fail
 233 expect() calls
Ran 9 tests across 1 file. [86.00ms]
bun sh/gh/check-runs.ts
python3 sh/gh/test-inventory.py
python3 bench/relay.test.py
......
----------------------------------------------------------------------
Ran 6 tests in 1.512s

OK
##[endgroup]
##[group]test gh
python3 sh/version.test.py
......error: recipe `test` was terminated on line 37 by signal 15
##[endgroup]
##[error]ci: test rs failed
##[error]Process completed with exit code 100.
##[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: 55d5a14fbac345e11237ed74183865b73f12304f
  CHECK_FAIL_FAST: 1
  NEXTEST_TEST_THREADS: 24
  MBX_CACHE_LINKS: 1
  MBX_REMOTE_URL: 
##[endgroup]
With the provided path, there will be 5 files 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 254437
Finished uploading artifact content to blob storage!
SHA256 digest of uploaded artifact is 65e9486be61f049948f70315b1cf427fecf52bab7a20e3be3e0065bf3daa74a7
Finalizing artifact upload
Artifact cargo-timings successfully finalized. Artifact ID 13286657576
Artifact cargo-timings has been successfully uploaded! Final size is 254437 bytes. Artifact ID is 13286657576
Artifact download URL: https://github.com/moq-dev/moq/actions/runs/41752982583/artifacts/13286657576
Post job cleanup.
##[start-action display=Run jdx/mr-boxington-action@88acf2bb50552c7c508cbac13e5f8c576b95b1de;id=__6137ba0d-327f-4b0c-a407-4bb49433070b.mbx]
##[end-action id=__6137ba0d-327f-4b0c-a407-4bb49433070b.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/eceae63f-11c4-45c8-be25-7ef00653cedb' 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 0 objects and 0 action results (0 B logical); 50.0 GiB logical remain
evicted 0 objects and 0 action results (0 B logical); 50.0 GiB logical remain
removed 1 target directories (34.6 GiB logical, 0 abandoned and 1 live); 0 B logical remain
Cleaning up orphan processes

(written by quest land watch)

@moq-bot

moq-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

codex / Codex land fixer note:

The queue failed in the origin panic supervision test. Failure log lines 3630–3635:

thread 'moq-ffi-worker' panicked at rs/moq-ffi/src/ffi.rs:558:17:
injected origin driver panic
thread 'origin::supervision_tests::a_panicked_origin_task_reaches_the_host_while_another_origin_keeps_working' panicked at rs/moq-ffi/src/origin.rs:688:9:
assertion failed: matches!(failed.next().await, Err(MoqError::Task { .. }))

The test and its supervision implementation are unchanged from base. The PR merges with base without conflicts. There is insufficient evidence to assign blame or confirm a flaky test. No PR branch was changed.

Local validation could not reach test execution: the affected-package test command failed because libspa-sys could not discover libpipewire-0.3. The focused moq-ffi nextest command also failed there; after supplying installed pkg-config and libclang paths, bindgen failed with fatal error: 'inttypes.h' file not found. A complete development environment is needed to establish a passing result.

(written by codex / Codex)

@moq-bot

moq-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

codex / Codex land fixer: Validation blocked: cargo test --locked -p moq-quic -p moq-tokio -p moq-uring -p moq-relay --tests failed with "The system library &#96;libpipewire-0.3&#96; required by crate &#96;libspa-sys&#96; was not found."; car. Recommendation: Rerun affected-package tests and the logged moq-ffi supervision test in a complete build environment; investigate the unchanged supervision code for a race if the assertion recurs.

    thread 'moq-ffi-worker' (1035695) panicked at rs/moq-ffi/src/ffi.rs:558:17:
    injected origin driver panic
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

    thread 'origin::supervision_tests::a_panicked_origin_task_reaches_the_host_while_another_origin_keeps_working' (1035639) panicked at rs/moq-ffi/src/origin.rs:688:9:
    assertion failed: matches!(failed.next().await, Err(MoqError::Task { .. }))

(written by codex / Codex)

# Conflicts:
#	Cargo.lock
#	rs/moq-tokio/MIGRATE.md
kixelated and others added 4 commits November 3, 2026 22:33
…ebTransport

- Tokio send reset reads the driver-recorded stop after the reset again, so a
  STOP_SENDING racing the reset still reports its code.
- Tokio's receive close watch returns a cached reset instead of a clean end.
- moq-quic does not report the end of a finished stream while leased bytes are
  unread, so a close watch no longer discards them.
- Both stacks drop a datagram that names no session, or another one, instead
  of failing the session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e driver

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	Cargo.lock
#	rs/moq-relay/Cargo.toml
@kixelated
kixelated added this pull request to the merge queue Nov 4, 2026

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

Changes since the prior pullfrog review at fd5d25e:

  • Fixed the tokio reset/stop race: reset_mut takes stream.stopped() before the reset again and resolves it after, so a STOP_SENDING that races the local reset still reports its code.
  • Fixed the cached reset close watch: the tokio driver's recv::poll_closed returns self.reader.reset before the FIN read path, keeping repeated closed() calls a reset.
  • Fixed the unread-lease FIN: moq-quic's Chunks::next stays blocked while Leased::has_unread(), so a finished stream no longer frees and discards leased bytes. Applies to both drivers and qmux.
  • Fixed stray datagrams: both WebTransport stacks now drop a datagram that names no session or another session instead of ending the session, with tokio and io_uring regression tests.
  • Added the ambient-clock timeout to the stray-datagram test and aligned io_uring's reset-watch/reset-stop comments with the driver.
  • Merged origin/main twice, resolving only Cargo.lock and rs/moq-tokio/MIGRATE.md (first) and Cargo.lock/rs/moq-relay/Cargo.toml (second) by hand.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

@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.

Automated review by review (OpenAI)

Reviewed head 24e2220, following fd5d25e.

The unread-lease and cached-reset findings are addressed: FIN no longer frees a stream while leased bytes remain unread, and the close watch checks the cached reset before reporting a clean end. The added regressions cover these paths. I found no additional actionable issue in this update.

Correction to my earlier canceled-open comparison: the old moq-net adapter also held an in-flight operation in a session slot, so that part was not new for adapter users. Direct native Session::open_bi previously held its partially opened streams in the future, whereas the new wrapper retains a canceled partial open in the session slot until adopted or the session closes. I have not established a further failure beyond that retained credit for a direct native caller.

Verification limits: GitHub-only static review of the delta, stream lifetime and tests, with an independent pass. No tests or runtime cancellation scenarios were run.

(Written by review (OpenAI))

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Nov 4, 2026
@moq-bot

moq-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

quest land watch: the merge queue removed this pull request at 24e2220 because it conflicts with main (merge_conflict).

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.

(written by quest land watch)

@moq-bot

moq-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

codex / Codex land fixer note:

The failure log reports:

4: Auto-merging quest/m2/net-wrap/README.md
5: CONFLICT (content): Merge conflict in quest/m2/net-wrap/README.md
6: Auto-merging rs/moq-tokio/src/client.rs
7: Automatic merge failed; fix conflicts and then commit the result.

The conflict can be resolved mechanically by keeping base’s runtime link to /quest/m2/spawn-audit.md and the PR’s description that both native WebTransport backends implement the transport traits. I tested that resolution locally. cargo build --locked -p moq-quic -p moq-tokio -p moq-uring -p moq-relay --features moq-relay/io-uring passed. cargo test --locked -p moq-quic -p moq-tokio -p moq-uring -p moq-relay --features moq-relay/io-uring exited 101 during test compilation:

The system library libpipewire-0.3 required by crate libspa-sys was not found.

The test dependency comes through workspace-hack. Test execution could not proceed, so the merge was aborted and the PR branch remains at its pinned head. Supply the missing development library and pkg-config metadata before retrying.

(written by codex / Codex)

@moq-bot

moq-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

codex / Codex land fixer: cargo test --locked -p moq-quic -p moq-tokio -p moq-uring -p moq-relay --features moq-relay/io-uring failed: "The system library &#96;libpipewire-0.3&#96; required by crate &#96;libspa-sys&#96; was not found."; canno. Recommendation: Provide the PipeWire development library and pkg-config metadata, merge base preserving its spawn-audit link and the PR’s completed WebTransport description, then rerun the build and tests.

Auto-merging quest/m2/net-wrap/README.md
CONFLICT (content): Merge conflict in quest/m2/net-wrap/README.md
Auto-merging rs/moq-tokio/src/client.rs
Automatic merge failed; fix conflicts and then commit the result.

(written by codex / Codex)

# Conflicts:
#	quest/m2/net-wrap/README.md
@kixelated
kixelated added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit d66c892 Nov 4, 2026
8 of 10 checks passed
@kixelated
kixelated deleted the quest/m1/quic-api/native-web branch November 4, 2026 09:16
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