Repository navigation
fix(moq-net): a recovered run leaves the copy's subscription - #8700
Conversation
Quest-Claim: 7bfad512518ff7e6
A run kept whole while a fill owed any of it kept its recovered part in the copy's ranges. Any later SUBSCRIBE_UPDATE reopens every lite-07 stale drop still in range, so a loosened budget let the publisher serve the recovered groups on both the recovery and the subscription. Rust and JS now carve what a recovery covers out of the ask. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
I reviewed the Rust and JS change that takes groups under recovery out of a lite-07 copy's demand subscription, along with its tests and the deleted quest file.
- Carve-out at the end of
RangeFills::poll/RangeFills.poll: whateverrecoveringstill covers is now removed fromgroupsafter the keep-whole and draining passes, so a later SUBSCRIBE_UPDATE can no longer reopen the stale drop while the recovery serves the same groups. The Rust and JS versions match:RangeSet::removein Rust and aremoveRunfold in JS. - Ordering is sound: the carve-out runs after
drainingandreadingare computed, so a draining run that overlaps a recovery still tracks its in-flight streams. Becauserecoveringis rebuilt bycover()minusreleased, groups that were handed back are not kept out by mistake, and refused groups stay out throughwaiting. - Tests: the new Rust
a_recovered_run_leaves_the_subscriptionchecks the ask shrinks from[1..5]to[3..5]once1..3is recovering. The JS assertion infills.test.tsnow expects the split[0..1], [3..4]instead of the whole run. - Known side effect: splitting an asked run makes
retire_skippeddrop that run'sskippedentry. At worst this costs one more re-ask of a skipped start, which is consistent with the PR's stated cost of one extra update.
claude-opus-5-5 | 𝕏
|
No actionable comments were generated in the recent review. WalkthroughJavaScript and Rust Estimated code review effort: Simple Merge Risk:
|

Completes A recovered run is served once (deleted here).
Summary
A lite-07 copy keeps an asked run whole in its subscription while any fill still owes part of it. A stale drop of part of that run opens a
Recoveryfor that part, but the part stayed in the copy's ranges. On lite-07 every SUBSCRIBE_UPDATE reopens each stale drop still in range. So an update that loosens the budget (for example a new live reader with a wider budget) let the publisher re-offer the recovered groups on the demand subscription while the recovery also served them. The copy then reset the duplicate stream.Fix, at the root and the same in Rust (
RangeFills::poll) and JS (RangeFills.poll): whatever a recovery still covers is removed from the ask, even out of a run otherwise kept whole. The update that narrows the ranges goes out as the recovery opens. The recovered groups were dropped on that subscription, so no stream arriving there is cut.Decisions (taken by the implementer, for review)
range_fill.rs, where it passed without the fix). Instead, the reproduction drives the copy's realRangeFillswith the subscriber's lite-07 harness and asserts the precondition of the duplicate: the recovered run is gone from what the next update carries. The publisher side (an update reopening a stale drop still in range) is already covered byan_update_reopens_every_stale_drop_and_supersedes_the_rest.Impact
Caused by
Validation
lite::subscriber::tests::a_recovered_run_leaves_the_subscription: fails with the fix reverted (ask stays[1..5]) and passes with it.range fills: a stale drop of asked groups is recovered once, on a subscription of its own: fails with the fix reverted, passes with it.bun test src/lite/injs/net: 866 pass, 0 fail.just check(scoped to the branch) passed one2cc4da.Follow-ups
None.
Models
Size: S
Implementer: Claude Code, claude-opus-5-5, unknown effort
(Written by Opus 5.5)
Review
Rewritten by
quest reviewafter each round.Head: e2cc4da
Base: origin/main at 98e54e7
Reviewer: Codex, gpt-6.1-sol, medium effort
Session:
01a1d790-fecb-7aa0-9e5e-721dbbc11ee8, round 1Score: 9/10,
The Rust and JS changes consistently exclude recovering groups from retained fill ranges, with no actionable regression identified by source inspection; builds and tests were not run as instructed.Rounds: 1; findings 0: fixed 0, refuted 0, open 0
Findings: none