Skip to content

fix(mux): an unset host clock publishes without a wall mapping - #5448

Merged
kixelated merged 2 commits into
releasefrom
quest/m0/release-22/wall-clock-omit
Oct 13, 2026
Merged

kixelated merged 2 commits into
releasefrom
quest/m0/release-22/wall-clock-omit

Conversation

@kixelated

@kixelated kixelated commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #5407.

Quest: quest/m0/release-22/wall-clock-omit (backport of #5429 to the 0.17 line).

Problem

On a host whose wall clock reads before 2020 (a device booted before NTP sync), moq_mux::Clock::new() panics, and Clock::new() runs in catalog::Config::default(). In the browser, @moq/publish builds its root clock with Catalog.u53 on a negative value the same way.

Fix

Follows the quest plan: keep moq-mux's public API, since release is moq-mux 0.10.x.

  • Clock stores its wall mapping as a private Option. Clock::new() leaves it None when the host wall clock can't be mapped, instead of panicking. Clock::at still refuses a bad explicit wall.
  • Producer::new advertises the root clock from that Option through a crate-private Clock::mapping(), so an unset host publishes with no root clock.
  • Clock::wall() keeps returning hang::catalog::Clock. It now documents that it panics when asked for the mapping on an unset host. That host already panicked earlier, during construction.
  • Clock::wall_clock returns UnmappableTimestamp when there is no mapping.
  • js/publish: pageRoot() mirrors main's change and omits clock when performance.timeOrigin is before 2020.

Parts of #5429 that don't apply here. Release has no importer re-anchoring (Timebase, Clock::arrival, Clock::placed, Listing::stamp), so these have nothing to port onto:

  • the whole-clock comparison in Listing::stamp and its PartialEq/Eq on Clock
  • the arrival range guard
  • the binary re-anchor test

On release the clock is fixed at construction and never compared.

Tests

  • clock::tests::an_unset_host_clock_has_no_wall
  • catalog::producer::test::an_unset_host_clock_publishes_without_a_wall
  • js/publish catalog producer omits the clock when the page's wall clock is unset

The Rust tests fake the host clock through the private fake_host_wall seam, as on main. Without the fix, both Rust tests panic at Clock::new() and the JS test fails. With it, all pass. just check origin/release passes.

Impact

  • Public API: none. cargo semver-checks -p moq-mux passes both against origin/release and against the published 0.10.13 with --release-type patch. The only new item, Clock::mapping, is pub(crate).
  • Behavior: Clock::wall() and Clock::wall_clock() now fail (panic and Err, respectively) only on an unset host, where construction used to abort.
  • Wire: none. The root clock was already optional.

Follow-ups

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

kixelated and others added 2 commits October 13, 2026 12:37
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backport of #5429 to the 0.17 line, keeping moq-mux's public API.
A host whose wall clock reads before 2020 now publishes without a root
catalog `clock` instead of panicking in `Clock::new()`. The js/publish
`CatalogProducer` omits `clock` the same way.

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

Copy link
Copy Markdown
Collaborator Author

Quest outcome: ported #5429 onto release with no change to the public API. The PR stays a draft until the maintainer decides.

Decisions for the maintainer:

  1. Whole-clock comparison and PartialEq on Clock: not ported. Release has no Listing::stamp or importer re-anchoring, so nothing compares clocks. Recommendation: leave them out.
  2. Clock::wall() panics on an unset host, as the quest plan decided. Recommendation: keep that.

Follow-up: once this merges, a small PR on main deletes the quest file and its Required entry in quest/m0/release-22/README.md, as for #5430.

(Written by Claude Opus 5.5)

@kixelated
kixelated marked this pull request as ready for review October 13, 2026 20:00
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 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-10-13T20:03:17.519130Z 2d72383 Draft marked ready
ℹ️ 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.

@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: 2d72383f52

ℹ️ 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".

effect.close();
} finally {
// Drops the override, uncovering the real getter.
Reflect.deleteProperty(performance, "timeOrigin");

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 Restore the original timeOrigin after the test

Under Bun, performance.timeOrigin is a configurable own data property rather than a prototype getter, so deleting this override leaves performance.timeOrigin as undefined. Every subsequent new CatalogProducer() in this file then computes a NaN wall value and throws in Catalog.u53, causing the remaining tests to fail. Save and restore the original property descriptor instead of deleting it.

AGENTS.md reference: js/AGENTS.md:L41-L41

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.

Not applying this one. Under Bun (v1.4.2, the repo's runner), performance has no own timeOrigin property: Object.getOwnPropertyDescriptor(performance, "timeOrigin") is undefined and the value comes from the prototype getter. Deleting the own override therefore uncovers that getter again rather than leaving undefined. bun test src/catalog.test.ts passes all 15 tests, including the later ones that build a CatalogProducer and would throw in Catalog.u53 on NaN. main uses the same cleanup (#5429).

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit 42ca851 into release Oct 13, 2026
7 checks passed
@kixelated
kixelated deleted the quest/m0/release-22/wall-clock-omit branch October 13, 2026 20:31
@kixelated

Copy link
Copy Markdown
Collaborator Author

Merge summary for 2d72383f528a922103bca9549e29adc35ae5987a.

Change: backports #5429 to release. A host whose wall clock reads before 2020 now builds moq_mux::Clock without a wall mapping and publishes with no root catalog clock, instead of panicking in Clock::new(). js/publish omits clock the same way. No public API or wire change.

Maintainer decisions (confirmed):

  1. Port only what exists on release: no whole-clock comparison, no PartialEq/Eq on Clock, no arrival range guard or re-anchor test, since release has no importer re-anchoring.
  2. Clock::wall() keeps its signature and panics only when an unset host is asked for its mapping.

Review: Codex reviewed this head. Its one finding (Bun's performance.timeOrigin cleanup) does not reproduce: Bun has no own timeOrigin property, so deleting the override restores the getter, and all 15 catalog.test.ts tests pass. I replied inline. I also checked that every remaining Clock::wall() caller on release is a test with an explicit clock, so no production path can hit the new panic.

CI: all green.

Follow-up: a PR onto main that deletes quest/m0/release-22/wall-clock-omit.md and its Required entry, as #5440 did.

(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