Skip to content

feat(net)!: an undeclared Rust timescale means untimed - #5083

Draft
kixelated wants to merge 5 commits into
mainfrom
quest/m1/rust-untimed-default
Draft

kixelated wants to merge 5 commits into
mainfrom
quest/m1/rust-untimed-default

Conversation

@kixelated

@kixelated kixelated commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

moq_net::track::Info::default() (and so create_track(name, None) and accept(None)) silently gave a track a millisecond timescale, while @moq/net treats an omitted timescale as untimed (#4968). The publish-timestamp and lite-07 untimed quests need each track to say whether it is timed before publishers stop inventing timestamps.

Approach

  • moq-net: removed impl Default for Timescale; Info::default() now has timescale: None. The lite encoder (TRACK_INFO, frame and datagram send times) and the lite datagram/fetch decoders name Timescale::MILLI for an untimed track, as does moq-audio's decode quantum floor.
  • Every Rust publisher that stamps frames declares milliseconds explicitly: hang catalog (Catalog::default_track_info, also used for MSF now), moq-mux timeline, TS SI tables (ts/si.rs, found by the moq-cli import/export tests), moq-boy status, moq-room chat, moq-stats JSON tracks, moq-bench, moq-c data_track, moq-ffi json/flate tracks, the moq-tokio and moq-json examples, and the moq-mux doc examples. Media tracks already pin their scale (hang::container::track_info, moq-c/moq-ffi raw tracks at microseconds).
  • Tests that write timestamps on a track created with None now use a millisecond track (Info::timed() inside moq-net, a local timed() helper elsewhere), preserving what they tested before. New regression test an_undeclared_timescale_is_untimed; broadcast_moq_lite_05_default_timescale became broadcast_moq_lite_05_untimed_track.
  • Docs: doc/setup/upgrade.md (Unreleased), doc/lib/js/net.md, rustdoc on Info, Info::with_timescale, create_track, hang::container::track_info. Quest file deleted and references updated; publish-timestamp and lite-untimed are now unblocked.
  • Follow-up on this branch: writers that still stamped a frame after create_track(name, None) now declare Timescale::MILLI. That includes the io_uring session and WebTransport tests, the relay io_uring runtime tests, and the benches and loom tests that write timestamps. Tracks that never stamp a frame stay untimed. Merged origin/main (through the C++ package landing).

Decision

CI failed because lite_session_over_the_worker writes Timestamp::ZERO on a track whose timescale is now untimed.

  • ✅ Keep the TimestampMismatch refusal. These are transport and bench paths, not untimed-track tests, so the track gets an explicit millisecond timescale, which None used to mean.
  • Skip the test, or accept a timestamp on an untimed track. Rejected.

Left for the maintainer, unchanged:

  1. Convenience constructor for timed tracks. Recommended: keep the with_timescale call; no new public API.
  2. The stale rs/moq-mux/AGENTS.md line. Agent files stay untouched unless a maintainer asks.
  3. TS SI tables stay on milliseconds. Settle a finer scale with the shared-clock quest.

Impact

  • Breaking (Rust API): moq_net::Timescale no longer implements Default.
  • Breaking (Rust behavior): track::Info::default(), broadcast::Producer::create_track(name, None), and track::Request::accept(None) create an untimed track; writing a timestamp to it fails with TimestampMismatch. Declare one with Info::default().with_timescale(Timescale::MILLI).
  • This follow-up adds no public API. The millisecond helpers are private to the tests and benches.
  • moq-ffi / moq-c / bindings: no API change; their data tracks stay millisecond-timed and raw/media tracks stay microsecond.
  • Wire: none. An untimed track on lite-05+ still declares milliseconds and carries send times, as before.

Alternatives

  • A public convenience such as track::Info::timed(scale) or a Timescale-to-Info conversion, so timed publishers don't write Info::default().with_timescale(...). Not added: no consumer needed new API to land this, and it can come with the publish-timestamp quest if wanted.
  • Rewriting tests that use Timestamp::ZERO as a placeholder to write untimed frames instead. Rejected: many depend on timestamps (expiry, retention), and a timed track keeps each test's original meaning.

Follow-ups

  • rs/moq-mux/AGENTS.md still says Info::default() is milliseconds; left alone since agent files need maintainer prompting.
  • TS SI tables keep the old millisecond scale; whether they should share the importer's microsecond scale belongs with the shared-clock work.

Tested

  • cargo nextest run --locked -p moq-uring --no-fail-fast --test session --test web -j 1: 13 passed, including lite_session_over_the_worker.
  • cargo nextest run --locked -p moq-relay --features io-uring --no-fail-fast --test runtime_uring -j 1: 6 passed.
  • quest check: 418 documents ok, after merging origin/main.
  • just check: passed on the timestamp fixes (5615 + 458 + 157 + 97 Rust tests, JS, Python). The later origin/main merge did not touch those writers.

(Written by Grok 4.7)

kixelated and others added 2 commits October 8, 2026 17:17
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`track::Info::default()`, `create_track(name, None)`, and `accept(None)`
now leave `timescale` as `None`, matching `@moq/net`. `Timescale` loses
`Default`; the lite encoder and datagram decoder name `Timescale::MILLI`
for an untimed track's send times, as does moq-audio's quantum floor.

Every publisher that stamps frames declares milliseconds explicitly: the
hang catalog, MSF and timeline tracks, TS SI tables, moq-boy status,
moq-room chat, moq-stats, moq-bench, moq-c and moq-ffi data tracks, and
the examples. Tests that write timestamps use a millisecond track.

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

Copy link
Copy Markdown
Collaborator Author

Quest outcome: implemented quest/m1/rust-untimed-default.md in full; left as a draft for the maintainer's decisions below.

Open decisions:

  1. Convenience constructor for timed tracks. Every timed publisher now writes Info::default().with_timescale(Timescale::MILLI), and about 60 test modules carry a local timed() helper.
    • (a) Keep as is. Recommended: no consumer needs new API yet.
    • (b) Add a public track::Info::timed(scale) (or similar) and swap the helpers for it.
    • (c) Revisit in the publish-timestamp quest, which touches the same publishers.
  2. Stale line in rs/moq-mux/AGENTS.md ("Info::default() is milliseconds"). The advice to pin with with_timescale still holds, but the reason is wrong now. Recommended: change it to "is untimed" in this PR once approved.
  3. TS SI table timescale. These tables keep milliseconds, which is today's behavior, while their PTS is finer. Recommended: keep milliseconds here and settle it with the shared-clock quest.

Issues found along the way: the TS SI tracks (rs/moq-mux/src/container/ts/si.rs) were a production path that relied on the old default, and only the moq-cli import/export integration tests caught it. Local moq-uring tests fail on RLIMIT_MEMLOCK (environment only).

(Written by Claude Opus 5.5)

kixelated and others added 3 commits October 9, 2026 11:17
…-default

Co-authored-by: Grok 4.7 <noreply@x.ai>
An undeclared timescale is untimed, and a timestamp on that track is TimestampMismatch. These session tests and benches are not untimed-track coverage, so the track declares Timescale::MILLI, which None used to mean.

Co-Authored-By: Grok 4.7 <noreply@x.ai>

This branch has not been deployed

No deployments
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