Merge main into dev - #4428
Merge main into dev#4428
Conversation
…tanding route (#4232) Co-authored-by: GPT-6 <noreply@openai.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: moq-bot[bot] <186640430+moq-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#4300) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…stroyed (#4290) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4299) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4272) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ounce doc (#4305) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4278) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…et (#4254) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: GPT-6 <noreply@openai.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…anicking (#4302) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4407) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…'s path (#4406) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…clients (#4411) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4405) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Carries main (through #4387) onto dev's breaking changes: #4350 and #4372 ported into rs/moq-c, main's auth fixes onto the moq-auth lease, and main's announce and track-cache changes onto dev's event and optional-retention APIs. Quests deleted on either side stay deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Recommendation: MERGEHead: Branch sync assessmentThis is an appropriate What changed (categories; ~666 files, +16.7k / −7.7k, 86 commits)
Spot-checks on the head match the claimed resolutions ( Complexity / alternativesWorth it: a documented merge-with-conflict-resolution is the right tool for divergent Caveats (do not block the sync itself)
Verdict: MERGE once CI is green (merge commit). This is an automated review, not the maintainer's decision |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cdeed8f0c6
ℹ️ 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".
| if (this.#state.closed.peek() !== undefined) { | ||
| dispose(); | ||
| return; |
There was a problem hiding this comment.
Release closed sinks when retention is unlimited
When Info.maxAge is omitted, #prune() intentionally never evicts cached groups. If the producer then closes, this branch disposes the only cleanup listener before removing the sink from each cached entry's mirrors map, so the cache permanently retains that sink and all of its mirrored groups. Ended tracks with unlimited retention therefore accumulate memory for every current subscriber, and late subscribers have the same lifetime problem; detach closed sinks from the cache without discarding the source groups.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Agreed on the close path: main's early return (keep mirrors tracked so they age out) meets dev's optional retention (nothing ages out). Fixed in ad8e3c0: with unlimited retention, a sink closed by the producer drops its mirror tracking and leaves the mirrors to its reader.
The late-subscriber half (a sink added after the track closed never registers a close listener) already exists on dev and isn't from this merge, so I'm leaving it for a follow-up rather than widening the sync.
(Written by Claude Opus 5.5)
Main keeps a sink's mirrors tracked after the producer closes so they age out with the cache. Dev's optional retention never ages anything out, so drop the tracking there and leave the mirrors to their reader. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ad8e3c0a76
ℹ️ 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".
| if (this.#state.closed.peek() !== undefined) { | ||
| if (this.#state.info.peek()?.maxAge === undefined) { | ||
| for (const entry of this.#cache) entry.mirrors.delete(sink); | ||
| } |
There was a problem hiding this comment.
Detach mirrors for subscribers created after closure
When subscribe() is called after an unlimited-retention producer has already closed, the outer if (closed === undefined) is skipped, so this cleanup callback is never installed; #addSink still mirrors every cached entry into the new sink and immediately closes it, leaving each forever-retained entry.mirrors map holding that sink and its mirrored group indefinitely. The fresh evidence in this revision is that the cleanup added for the prior finding exists only inside the live-sink callback, so late subscribers still leak once per cached group.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This path (a sink added after the track closed never registers a close listener) is the same on dev before this merge, so it isn't a merge interaction. Keeping the sync focused and leaving it for a follow-up on dev.
(Written by Claude Opus 5.5)
… event Dev's announce stream can yield `live` before the relay has learned the publisher's route, so the JS subscriber raced a subscribe into Unroutable. Wait for the `age` start, as the Rust arm already does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merge summary:
Merging with a merge commit. (Written by Claude Opus 5.5) |
Merges
main(through #4387, e713c30) intodev. dev keeps its breaking changes; main's fixes and features are carried onto them.Notable conflict resolutions:
cpp_compat: true, so the generatedmoq.hopensextern "C") and fix(libmoq): render moq.pc only when packaging #4372 (build.rsno longer renders a pkg-config file) intors/moq-c/build.rs.nix/overlay.nixrenders and checksmoq-c.pcat install time fromrs/moq-c/moq-c.pc.in, usingcleanCargoSourcelike main. Comments inCMakeLists.txt,native-libs/apple.txt,moq-c.pc.in,rs/justfile,test/interop/interop.sh,README.md,doc/lib/c, andCONTRIBUTING.md(the OBS release tag) namemoq-cinstead of libmoq. Thers/libmoqstub keeps dev's README; its version follows main's release.Key::verifykeeps dev'sdecode::<C>split and gains main's exactvalidate_times(exp <= now,nbf > now).serve::Policy::decideuses main's SETUP-tokenjwt()selection and JWT-plus-certificate refusal, then dev'sverify()(which adds--key-set). Dev's gateway-limits test sets a revalidate cadence, which main'sServer::newrequires with limits. Docs merge both (--key-set,narrowed/shutdownend reasons, and fix(auth): make grant expiry exact, dropping the clock-skew grace #4368's exact expiry).verifymoved fromjose.jwtVerifytocompactVerifyplus a manualexpcheck, which dropped thenbfcheck main's fix(auth)!: restore 0.14 auth parity #4319 tests rely on. It now refusesnbf > now, matching Rust. The raw-signed test tokens carry the key'skid, which dev'sdecoderequires.suffix()); the IETFapply_updatehelper takes main'ssuffix = update.prefix,path = prefix.join(suffix).OriginStatekeeps dev'sreplayingsources and main'swithdrawnpeers. Main'spublic_rules.rs,mesh_withdraw.rs, thesessionbench, andjs/net/bench/forward.tsuse dev'sStart/Update/End/Liveevents.maxAgeis unset, per dev;DEFAULT_MAX_AGE_MSstays removed. fix(net): start a guarded group write only while the group is in budget #4254's stale-group publisher test declaresmaxAge: 5000, the old default, since dev has none.newOrigin(t)also covers dev'sTestAnnouncedYieldsLiveOnceCaughtUp.platform.yml(ci: compile the Windows, macOS, and OBS plugin code on every PR #4370);obs.ymlandnightly.ymluse main's.#obsshell (build(nix): move obs-studio to a .#obs shell, drop rust-docs #4410) under moq-c names;interop.ymlruns both dev'smax-ageand main'sharnesssteps.m0/release,m1/close-codes,m1/libmoq-{fetch,shutdown},m1/release-size,m2/capture-clock-source, and the rest of main's chore(quest): audit the quest tree #4393 audit). Main's audit wins the READMEs and the reshaped quests, with libmoq renamed to moq-c where it names the hand-written crate. Dev-only quests stay listed inm1/README.md(page-load marker, empty state, JS active count, libmoq stub retirement, capture cut test, NVENC keyframe flag). Dev'sm1/origin-mountis deleted, done by feat(net): read a subtree through an origin mount #4271 on main.quest checkpasses.Left for a follow-up:
m1/announce-live-apps(main) overlaps dev'sm1/announce-page-loadandm1/announce-empty-state, and says the page-load hold ends when the first dial gives up, while open quest: announce streams toggle between live and offline #4369 records "no give-up". Both are kept; fold them once quest: announce streams toggle between live and offline #4369 lands.Public API / wire impact: none beyond what the merged
mainanddevPRs already carry.Added after review:
devagain for fix: bump qmux to 0.6.1 so ws:// keeps the close code #4398 (qmux 0.6.1), which fixes themoq-tokioclose_codefailures.agestart instead of the first announce event, which can be dev'slivemarker before the relay learns the route (it raced intoUnroutable).Checks:
nix develop --command just check origin/dev,just test max-age, and thejs/netsuite pass locally.Merge with a merge commit, not squash.
(Written by Claude Opus 5.5)
🤖 Generated with Claude Code