From 797235afd3d8d2f0a6c5ea7fd659a44741d05a24 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 29 Sep 2026 08:41:40 -0700 Subject: [PATCH 1/3] chore(quest): delete the announce live marker instead of patching it Co-Authored-By: Claude Opus 5.5 --- quest/m1/README.md | 6 ++-- quest/m1/announce-empty-state.md | 16 ---------- quest/m1/announce-live-apps.md | 36 ---------------------- quest/m1/announce-live-removal.md | 51 +++++++++++++++++++++++++++++++ quest/m1/announce-page-load.md | 19 ------------ quest/m1/cli-announced.md | 37 ++++++++++++++++++++++ quest/m1/js-active-count.md | 23 -------------- 7 files changed, 90 insertions(+), 98 deletions(-) delete mode 100644 quest/m1/announce-empty-state.md delete mode 100644 quest/m1/announce-live-apps.md create mode 100644 quest/m1/announce-live-removal.md delete mode 100644 quest/m1/announce-page-load.md create mode 100644 quest/m1/cli-announced.md delete mode 100644 quest/m1/js-active-count.md diff --git a/quest/m1/README.md b/quest/m1/README.md index 55815278e4..7d86d53c9a 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -29,10 +29,8 @@ transport, benchmark tooling); worktrees isolate commits, not semantics. - [Graceful close in bindings](/quest/m1/bindings-graceful-close.md) - on dev, `shutdown` drains a session in moq-ffi and every wrapper like Rust, so the wrappers keep the tail of a publish - [Raw stream codes](/quest/m1/raw-stream-codes.md) - raw QUIC stream resets and stops carry the application's code, not an HTTP/3-mapped one - [WebTransport close upstream](/quest/m1/wt-close-upstream.md) - web-transport-moq delivers the close capsule itself, and moq-tokio's `CLOSE_LINGER` is deleted -- [Live in apps](/quest/m1/announce-live-apps.md) - the demo and `@moq/room` show "no broadcasts" from the `live` marker, which waits for the first session on page load -- [Page-load marker](/quest/m1/announce-page-load.md) - an announcement stream opened before the first connection waits for its replay before `live` -- [Empty state](/quest/m1/announce-empty-state.md) - watch, room, and the demo show "no broadcasts" once `live` arrives with nothing announced -- [JS active count](/quest/m1/js-active-count.md) - @moq/net speaks MoQ Active Count, so its IETF announce consumers go live without a timer +- [moq announced](/quest/m1/cli-announced.md) - `moq ls` becomes a follow-only live view named `moq announced`, with no remote path completion +- [Delete the live marker](/quest/m1/announce-live-removal.md) - announce streams yield only route events; the `Live` marker and its hold machinery are gone - [IETF hidden default](/quest/m1/ietf-hidden-default.md) - a moq-transport peer without the MoQ Hidden option is advertised hidden namespaces; one with it filters per subscription - [Watch refusal](/quest/m1/watch-refusal.md) - `` shows an origin refusal as an error instead of sitting offline - [moqsink recoverable errors](/quest/m1/moqsink-keyframe-latch.md) - a leading delta or a timestamp rewind drops frames until a keyframe instead of invalidating a moqsink pad diff --git a/quest/m1/announce-empty-state.md b/quest/m1/announce-empty-state.md deleted file mode 100644 index 54e4adbba8..0000000000 --- a/quest/m1/announce-empty-state.md +++ /dev/null @@ -1,16 +0,0 @@ -# [S] Watch, room, and the demo show when nothing is live - -## Goal - -Once an announcement stream reports `live` with nothing announced, `js/watch`, -`js/room`, and `demo/web` show a "no broadcasts" state instead of a spinner -that never resolves. - -## Plan - -- Today each consumer skips the `live` marker. Track it next to the - announced set and render the empty state only after it arrives. - -## Required - -- [Page-load marker](/quest/m1/announce-page-load.md) - otherwise the empty state flashes on page load before the first connection replays diff --git a/quest/m1/announce-live-apps.md b/quest/m1/announce-live-apps.md deleted file mode 100644 index c6f9969516..0000000000 --- a/quest/m1/announce-live-apps.md +++ /dev/null @@ -1,36 +0,0 @@ -# [M] Apps show "no broadcasts" from the live marker - -## Goal - -A browser page listing broadcasts shows an empty state once the relay has -said there are none, never a spinner that never resolves and never a false -empty state before the first session answered. The demo watch page and -`@moq/room` use `@moq/net`'s `live` marker, and `@moq/net` settles when an -origin stream opened before the first connection goes live. - -## Plan - -- #4261 (on `dev`) adds the `live` event, and #4266 the same marker in the - bindings. Open #4384 renames the announce events to Start/Update/End/Live; - follow its names if it lands first. The consumers it touches - (`demo/web/src/index.ts`, `js/room/src/room.ts`, `js/watch/src/broadcast.ts`, - `js/moq-boy`) skip it today. -- Page load: an origin stream opened before any session connects has no - session to wait on, so today it goes `live` at once and broadcasts arrive - after it. Settled: the reconnect loop (`js/net/src/connection/reload.ts`), - which already answers requests through `expect()`, holds the marker until - its first session lands `live` or its first dial gives up. An empty list - then means the relay said so or is unreachable, which a UI can tell apart. - Once a session is up, its own `live` ends the hold: every wire guarantees - one (ANNOUNCE_OK, ANNOUNCE_INIT, or the quiet-stream fallback). A peer that - accepts the announce stream and never answers is a peer bug, so no extra - timeout. - Check whether Rust's reconnecting client has the same gap. -- Apps: loading before `live`, an explicit empty state after it with nothing - announced, and an error state when the connection gives up. Libraries - expose the state as a signal; wording stays in the demo. -- Tests: an origin stream opened before connect is not `live` until the first - session is; a connection that cannot connect ends the wait. - -Public API: when `@moq/net` emits `live` changes; any state signal on -`@moq/room` or `@moq/watch` is additive. Lands on `dev` with #4261. Wire: none. diff --git a/quest/m1/announce-live-removal.md b/quest/m1/announce-live-removal.md new file mode 100644 index 0000000000..c58d2c7d13 --- /dev/null +++ b/quest/m1/announce-live-removal.md @@ -0,0 +1,51 @@ +# [L] Delete the announce live marker + +## Goal + +Announce streams yield only route events (start, update, end) in +`rs/moq-net`, `@moq/net`, moq-ffi, and every binding. The `Live` marker and +everything that exists to produce it are deleted: the per-session replay +holds and landing bookkeeping on the origin, the quiet-period guesses on +wires without an exact count, and the marker in the bindings' event types. + +## Plan + +Decided 2026-09-29 by the maintainer, after walking through a page-load fix, +a Live/Offline toggle, and a per-session `live()`: + +- The marker answers "has the initial list arrived?" Only one-shot listing + (`moq ls`, shell completion) uses it, and no app does: room, watch, and the + demo skip it. An origin merges many sessions and local publishers, so + "caught up" there needs aggregation across connections that start, fail, + and reconnect independently. That produced the page-load race, the special + first connection, and the offline question. With no customer, delete it + rather than patch it. +- No replacement for now. A single-connection listing (a `session.live()` + returning the peer's initial set) can come back when a customer needs it. +- Apps drive loading and offline UI from connection status, which `@moq/net` + already exposes. No empty-state work is planned. +- Wire unchanged. lite's ANNOUNCE_OK Active Count and the IETF Active Count + extension (`drafts/draft-lcurley-moq-active-count.md`) stay on the wire, + unused; a future listing would read exactly that count. Peers still send + and validate it. Only the local consumption goes. + +Guidance: + +- The marker lives on `dev` only (Rust since #4059, JS since #4261, bindings + since #4266), so this deletion breaks nothing published. +- Look for code that only exists to produce the marker, and delete it rather + than stubbing it: replay/landing counters in the origin, holds taken by the + JS forwarder and reconnect loop, quiet-gap timers on lite-03/04 and + moq-transport, and the batching of the initial set if nothing else needs + it. Keep what other features rely on (for example, request answering + through `expect()`). +- Update `doc/lib/{rs,js}`, `doc/concept`, the binding docs, and examples + (`js/net/examples/discovery.ts`) inline. Delete marker-only tests. +- Run `just test interop --all`. + +Public API: removes `AnnounceEvent::Live` / `{ kind: "live" }` / +`MoqAnnounceEvent::Live` and the binding aliases, on `dev`. Wire: none. + +## Required + +- [moq announced](/quest/m1/cli-announced.md) - the CLI stops reading the marker first diff --git a/quest/m1/announce-page-load.md b/quest/m1/announce-page-load.md deleted file mode 100644 index 8cf4b4cfb5..0000000000 --- a/quest/m1/announce-page-load.md +++ /dev/null @@ -1,19 +0,0 @@ -# [S] The live marker waits for the first connection on page load - -## Goal - -An announcement stream opened before the first session connects does not -report `live` until that session's replay lands, or the connection attempt -gives up. Today it has no session to wait on, so it goes `live` at once and -the broadcasts arrive after it, in both `@moq/net` and `rs/moq-net`. - -## Plan - -- Open question for the maintainer: what counts as giving up (the first failed - attempt, the backoff ceiling, or never). -- The JS reconnect loop (`js/net/src/connection/reload.ts`) already counts as - an answerer for requests through `expect()`; it can also take a replay hold - on the origin until its first session lands or it gives up. -- Rust has no reconnect loop of its own; decide whether an app that opens a - stream before connecting needs an equivalent hold there. -- Add a caught-up test that opens the stream before the first connection. diff --git a/quest/m1/cli-announced.md b/quest/m1/cli-announced.md new file mode 100644 index 0000000000..e98b61370d --- /dev/null +++ b/quest/m1/cli-announced.md @@ -0,0 +1,37 @@ +# [M] moq announced follows announcements live + +## Goal + +`moq ls` becomes `moq announced`, which only follows. On a terminal it +redraws a list of what is announced right now, updating on each start and +end. When piped, or with `--json`, it prints one `+`/`-` line per event, as +`moq ls --follow` does today. `moq ls` and its one-shot mode are gone, and so +is shell completion of remote broadcast paths. Nothing in `rs/moq-cli` +reads the announce `Live` marker afterwards. + +## Plan + +Decided 2026-09-29 by the maintainer: + +- Follow-only. A one-shot listing needs to know when the initial set is + complete, which is the only thing the `Live` marker is used for. No + customer needs a one-shot listing, so it waits until one does. Why: + [Delete the live marker](/quest/m1/announce-live-removal.md) removes that + marker because an origin fed by many sessions can't answer "caught up" + honestly. +- The name says what it shows: announcements, as they happen. +- No `ls` alias: it becomes an unknown command, per the no-compat-shim rule. +- Shell completion (`rs/moq-cli/src/complete.rs`) drops remote broadcast + paths and completes flags and subcommands only, with no network. It waits + on `Live` today and would otherwise need a timeout. +- The live view is plain terminal redraw. Prefer a maintained crate if the + redraw grows beyond a few lines. Keep the event-line output byte-for-byte + compatible with today's `--follow` output so scripts only change the + command name. + +Update every invocation: `doc/bin/cli.md`, `doc/bin/inspect.md`, +`doc/bin/relay/http.md`, `doc/concept/moq-lite.md`, demo recipes, and +anything else a repo-wide search for `moq ls` finds. Check them against +`--help`. moq-cli takes a patch bump. + +Public API: moq-cli command rename, which is breaking for scripts. Wire: none. diff --git a/quest/m1/js-active-count.md b/quest/m1/js-active-count.md deleted file mode 100644 index 1730c213c9..0000000000 --- a/quest/m1/js-active-count.md +++ /dev/null @@ -1,23 +0,0 @@ -# [S] @moq/net counts an IETF namespace subscription until it is caught up - -## Goal - -`@moq/net` speaks the MoQ Active Count extension -(`drafts/draft-lcurley-moq-active-count.md`) on draft-16+, as `rs/moq-net` -does: it declares the ACTIVE_COUNT option, its publisher puts on the REQUEST_OK -answering SUBSCRIBE_NAMESPACE how many NAMESPACE messages come before the -subscription is caught up, and its subscriber lands that stream's source on -the count instead of the quiet timer. - -## Plan - -- Mirror `rs/moq-net/src/ietf/active_count.rs` in `js/net/src/ietf/`, like - `hidden.ts`: declare on draft-16+ only, and read the peer's declaration. -- Publisher: count what it advertises before writing REQUEST_OK, then send - exactly those NAMESPACE messages first. -- Subscriber: a missing count when negotiated, one when not, or one on any - other REQUEST_OK is a protocol violation, as in Rust. -- Extend the IETF cases of the JS caught-up tests to show the counted versions - land without waiting out the quiet gap. - -Public API: none. Wire: the extension, already specified. From 756e57bf477bab99364b0c5f1900811e035c60d7 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 29 Sep 2026 09:31:35 -0700 Subject: [PATCH 2/3] chore(quest): drop the moq-cli bump and the stale AnnounceEventLive example Co-Authored-By: Claude Opus 5.5 --- quest/m1/cli-announced.md | 2 +- quest/m1/ffi-shape/net.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/quest/m1/cli-announced.md b/quest/m1/cli-announced.md index e98b61370d..2b0a745ece 100644 --- a/quest/m1/cli-announced.md +++ b/quest/m1/cli-announced.md @@ -32,6 +32,6 @@ Decided 2026-09-29 by the maintainer: Update every invocation: `doc/bin/cli.md`, `doc/bin/inspect.md`, `doc/bin/relay/http.md`, `doc/concept/moq-lite.md`, demo recipes, and anything else a repo-wide search for `moq ls` finds. Check them against -`--help`. moq-cli takes a patch bump. +`--help`. Public API: moq-cli command rename, which is breaking for scripts. Wire: none. diff --git a/quest/m1/ffi-shape/net.md b/quest/m1/ffi-shape/net.md index a06f50d831..83f99cc8af 100644 --- a/quest/m1/ffi-shape/net.md +++ b/quest/m1/ffi-shape/net.md @@ -20,7 +20,7 @@ are renamed. - Objects that are only getters become records. Handles with verbs (`Request`, `TrackRequest`, `GroupRequest`) stay objects. - An enum whose variants a wrapper must name spells each variant - `` (`AnnounceEventStart`, `AnnounceEventLive`) in Go, + `` (`AnnounceEventStart`, `AnnounceEventEnd`) in Go, Kotlin, Dart, and Python, whatever the generated name. Swift keeps its generated `.` cases, since it cannot alias a case. - `TrackProducer` drops `name`/`is_used`/`used`/`unused` for `demand()`. From d944d93b62a8b2a893446a6530ac3feec287f80d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 29 Sep 2026 09:34:45 -0700 Subject: [PATCH 3/3] chore(quest): keep local and rendition completion, drop only broadcast paths Co-Authored-By: Claude Opus 5.5 --- quest/m1/cli-announced.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/quest/m1/cli-announced.md b/quest/m1/cli-announced.md index 2b0a745ece..8b65551a42 100644 --- a/quest/m1/cli-announced.md +++ b/quest/m1/cli-announced.md @@ -21,9 +21,10 @@ Decided 2026-09-29 by the maintainer: honestly. - The name says what it shows: announcements, as they happen. - No `ls` alias: it becomes an unknown command, per the no-compat-shim rule. -- Shell completion (`rs/moq-cli/src/complete.rs`) drops remote broadcast - paths and completes flags and subcommands only, with no network. It waits - on `Live` today and would otherwise need a timeout. +- Shell completion (`rs/moq-cli/src/complete.rs`) drops the `BROADCAST` + completer, which waits on `Live` today and would otherwise need a timeout. + The other completers (local capture devices, catalog renditions) don't read + `Live` and stay. - The live view is plain terminal redraw. Prefer a maintained crate if the redraw grows beyond a few lines. Keep the event-line output byte-for-byte compatible with today's `--follow` output so scripts only change the