From f8a2852098ce0a341853026ed2919711a7b0c244 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 10 Oct 2026 08:14:21 -0700 Subject: [PATCH 1/3] quest: plan moq fetch close, terminal dial errors, and the interop close log Co-Authored-By: Claude Opus 5.5 --- quest/m1/README.md | 3 +++ quest/m1/interop-close-log.md | 22 ++++++++++++++++++++++ quest/m1/moq-fetch-close.md | 20 ++++++++++++++++++++ quest/m1/moq-tokio-terminal-errors.md | 23 +++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 quest/m1/interop-close-log.md create mode 100644 quest/m1/moq-fetch-close.md create mode 100644 quest/m1/moq-tokio-terminal-errors.md diff --git a/quest/m1/README.md b/quest/m1/README.md index e06cb20693..49abe1819c 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -201,3 +201,6 @@ ladders. - [WebGPU on Safari](/quest/m1/webgpu-safari.md) - the WebGPU renderer is verified on Safari 26 for macOS and iOS - [A/V sync across a break](/quest/m1/watch-break-av-sync.md) - `@moq/watch` never plays pre-break audio out of sync with pre-break video - [moqsrc reconnect](/quest/m1/moqsrc-reconnect.md) - `moqsrc` redials after losing its relay and resumes on the same pads +- [moq fetch closes cleanly](/quest/m1/moq-fetch-close.md) - `moq fetch` closes its session before exiting instead of leaving the relay to time it out +- [Terminal dial errors](/quest/m1/moq-tokio-terminal-errors.md) - moq-tokio's reconnect loop stops on errors that can never succeed +- [Interop close log](/quest/m1/interop-close-log.md) - the interop idle-out check reads the relay close log at a pinned level diff --git a/quest/m1/interop-close-log.md b/quest/m1/interop-close-log.md new file mode 100644 index 0000000000..1cc0fbc10e --- /dev/null +++ b/quest/m1/interop-close-log.md @@ -0,0 +1,22 @@ +# [XS] The interop idle-out check reads the relay's close log at a fixed level + +## Goal + +The interop harness's idle-out check never misreads a clean close as an open +connection because of the relay's log level. + +## Plan + +Found while merging #5174 (2026-10-10): the relay logs a clean +`connection closed` at info and an error close at warn, and the harness +counts those lines to decide every connection closed. If the interop relay +ever runs at warn, every clean close would read as still open and fail the +round. + +Decided 2026-10-10: pin the harness's relay log filter for the close-log +target to info in `test/interop/interop.sh`, so the product keeps warn for +error closes. Rejected: logging both at one level in moq-relay. Add a check +that fails loudly if the relay emits no close line for a connection the +harness saw open. + +Public API: none. Wire: none. diff --git a/quest/m1/moq-fetch-close.md b/quest/m1/moq-fetch-close.md new file mode 100644 index 0000000000..8ee20da1fe --- /dev/null +++ b/quest/m1/moq-fetch-close.md @@ -0,0 +1,20 @@ +# [XS] moq fetch closes its connection cleanly + +## Goal + +`moq fetch` (`rs/moq-cli/src/fetch.rs`) closes its session before exiting, +so the relay sees a clean close instead of timing the connection out. + +## Plan + +Found while merging #5174 (2026-10-10), which taught every interop client to +close cleanly and made the harness fail a round on an idled-out connection. +`moq fetch` still drops its connection without `Connection::close` or +`Client::close`; the js-native FETCH_DATA wire-compat cell reproduced the +relay timing it out. Only the wire-compat lanes reach it, and they skip the +idle-out check, so it doesn't fail CI today. + +Close the session the way #5174 did for the other clients, then turn the +idle-out check on for the wire-compat FETCH cells as the regression. + +Public API: none. Wire: none. diff --git a/quest/m1/moq-tokio-terminal-errors.md b/quest/m1/moq-tokio-terminal-errors.md new file mode 100644 index 0000000000..3a25ddf520 --- /dev/null +++ b/quest/m1/moq-tokio-terminal-errors.md @@ -0,0 +1,23 @@ +# [S] moq-tokio's reconnect loop gives up on errors that can never succeed + +## Goal + +With unlimited backoff, moq-tokio's reconnect loop stops and reports an error +that can never succeed on retry (a local dial or configuration error such as +`UnsupportedScheme`, an invalid URL, or a bad TLS configuration) instead of +retrying it forever. Transient errors keep retrying. + +## Plan + +Found while planning [moqsrc reconnect](/quest/m1/moqsrc-reconnect.md) +(2026-10-10): `moqsink` already reconnects with `backoff.timeout = 0`, and a +mistyped scheme makes it retry forever without a bus error. `moqsrc` will do +the same once it reconnects. + +Classify dial errors at the source: the loop already treats non-retryable +CONNECT statuses (`status_retryable`) as terminal; extend that to local +errors, listed by type rather than by message. Fail loud with the error. +Test with mocked time: an unsupported scheme fails at once under unlimited +backoff, and a refused TCP connect still retries. + +Public API: behavior only (the loop ends with the error). Wire: none. From 2751af01304c9fabd6c97ebd7e76701f133a0e81 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 10 Oct 2026 08:22:53 -0700 Subject: [PATCH 2/3] quest: scope the fetch close regression to current binaries Address review: the wire-compat FETCH cells run released binaries the idle-out check excludes, so the regression runs current relay and CLI. Close on every exit path, and fail a round whose relay log shows no connection. Co-Authored-By: Claude Opus 5.5 --- quest/m1/interop-close-log.md | 6 +++--- quest/m1/moq-fetch-close.md | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/quest/m1/interop-close-log.md b/quest/m1/interop-close-log.md index 1cc0fbc10e..8268fe0a97 100644 --- a/quest/m1/interop-close-log.md +++ b/quest/m1/interop-close-log.md @@ -15,8 +15,8 @@ round. Decided 2026-10-10: pin the harness's relay log filter for the close-log target to info in `test/interop/interop.sh`, so the product keeps warn for -error closes. Rejected: logging both at one level in moq-relay. Add a check -that fails loudly if the relay emits no close line for a connection the -harness saw open. +error closes. Rejected: logging both at one level in moq-relay. Also fail +the round loudly when the relay log shows no connection for it, so a filter +that hides the `conn{id=...}` and close lines can't pass as nothing open. Public API: none. Wire: none. diff --git a/quest/m1/moq-fetch-close.md b/quest/m1/moq-fetch-close.md index 8ee20da1fe..005c7fb274 100644 --- a/quest/m1/moq-fetch-close.md +++ b/quest/m1/moq-fetch-close.md @@ -14,7 +14,11 @@ close cleanly and made the harness fail a round on an idled-out connection. relay timing it out. Only the wire-compat lanes reach it, and they skip the idle-out check, so it doesn't fail CI today. -Close the session the way #5174 did for the other clients, then turn the -idle-out check on for the wire-compat FETCH cells as the regression. +Close the session the way #5174 did for the other clients, on every exit +after connecting, including an error or the `timeout_at` deadline. For the +regression, run a FETCH round with the current relay and the current +`moq fetch` under the idle-out check. Don't enable the check for the +wire-compat cells: they mix in released relays and clients, which it excludes +because they may not close cleanly or log which connection closed. Public API: none. Wire: none. From cb3ee2c08719bbc6cb496a9789008c293522472a Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 10 Oct 2026 08:27:02 -0700 Subject: [PATCH 3/3] quest: terminal only when every raced transport fails; the fetch close round is new Co-Authored-By: Claude Opus 5.5 --- quest/m1/moq-fetch-close.md | 8 +++++--- quest/m1/moq-tokio-terminal-errors.md | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/quest/m1/moq-fetch-close.md b/quest/m1/moq-fetch-close.md index 005c7fb274..2c8216e458 100644 --- a/quest/m1/moq-fetch-close.md +++ b/quest/m1/moq-fetch-close.md @@ -17,8 +17,10 @@ idle-out check, so it doesn't fail CI today. Close the session the way #5174 did for the other clients, on every exit after connecting, including an error or the `timeout_at` deadline. For the regression, run a FETCH round with the current relay and the current -`moq fetch` under the idle-out check. Don't enable the check for the -wire-compat cells: they mix in released relays and clients, which it excludes -because they may not close cleanly or log which connection closed. +`moq fetch` under the idle-out check. No such round exists today (FETCH only +runs in the wire-compat lanes), so add one and wire it into CI. Don't enable +the check for the wire-compat cells: they mix in released relays and clients, +which it excludes because they may not close cleanly or log which connection +closed. Public API: none. Wire: none. diff --git a/quest/m1/moq-tokio-terminal-errors.md b/quest/m1/moq-tokio-terminal-errors.md index 3a25ddf520..7a216b9f8d 100644 --- a/quest/m1/moq-tokio-terminal-errors.md +++ b/quest/m1/moq-tokio-terminal-errors.md @@ -16,7 +16,11 @@ the same once it reconnects. Classify dial errors at the source: the loop already treats non-retryable CONNECT statuses (`status_retryable`) as terminal; extend that to local -errors, listed by type rather than by message. Fail loud with the error. +errors, listed by type rather than by message. The dial races QUIC and +WebSocket, so a local error is terminal only when every raced transport fails +terminally, as the CONNECT status check already requires; one transport +rejecting a scheme the other accepts must keep dialing. Fail loud with the +error. Test with mocked time: an unsupported scheme fails at once under unlimited backoff, and a refused TCP connect still retries.