Skip to content

quest: plan the passive-reader spin, retry intervals, and a real-clock test guard - #8043

Merged
kixelated merged 2 commits into
mainfrom
quest-plan/follow-ups-1106
Nov 6, 2026
Merged

kixelated merged 2 commits into
mainfrom
quest-plan/follow-ups-1106

Conversation

@kixelated

@kixelated kixelated commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Plans the follow-ups from today's /quest-complete run, and a guard plus burn-down for tests that wait on the real clock, which the maintainer asked to have audited.

Quests

Audit summary

The lints from #7329 ban how a timer is spelled, not the clock it runs on. kio::clock::sleep and @moq/clock's sleep are the approved replacements, and they still wait in real time unless a test installs a mocked clock. Nothing makes a mocked clock the default: 610 of 4,369 JS tests and 3,288 of 4,756 Rust async tests opt in. Biome skips test/** and the Bun global. The written rule left root AGENTS.md in #7761, so online reviewers stopped seeing it. When reviewers did flag a wait (#4039, #3641, #1717, #7845), it usually survived anyway: the fix landed in one language but not its siblings, or swapped one real wait for another (#7845's idle()).

The scan found:

  • Negative proofs by waiting: Rust 64, JS 110.
  • Settle sleeps: Rust 47, JS 56.
  • Elapsed-time assertions: Rust 20, JS 4.
  • Left alone: about 530 progress-under-timeout tests and about 150 real-I/O tests.

Decisions (maintainer, 2026-11-06)

Lite subscriber spin: goal and milestone?

  • ✅ As stated, m0 (a relay can burn a core in shipped code)
  • m1
  • m3

Copy-fill flake?

  • ✅ As stated, a test-flakes-2 child, plus "investigate how we keep having tests that use real-time? Audit how these get in here and what other tests need to be mocked still."
  • Skip it

Over-limit PUBLISH retry interval?

Forwarding an upstream refusal's interval?

  • ✅ Forward it verbatim (m1)
  • Make from_code lossless
  • Don't plan it

JS guard?

  • ✅ Fail on a real timer, plus the REVIEW.md rule and denying Bun in biome
  • A grep check in just check
  • A review rule only

Rust guard?

  • ✅ Spike the guard, with a grep check as the fallback
  • A grep check only
  • JS only for now

Burn-down scope?

  • ✅ Negative proofs, settle sleeps, and elapsed-time assertions
  • Negative proofs and settle sleeps only
  • Everything on the real clock

Milestone?

  • ✅ Guard m0, burn-down m1
  • All m0
  • All m1

Impact

Planning only. Public API: none. Wire: none.

Review

Codex gpt-6.1-sol (medium), round 1 on head 631b5fe against origin/main 4722e76: no findings. Checks: quest check --base origin/main and sh/markdown.sh check pass.

(Written by Claude Opus 5.5 with high effort)

🤖 Generated with Claude Code

kixelated and others added 2 commits November 6, 2026 09:32
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t wait on it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 6, 2026 18:39
@coderabbitai

coderabbitai Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 17 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8c0c5b58-f9c0-4035-9881-8c21b31c43ac
Commits

Reviewing files that changed from the base of the PR and between 4722e76 and 631b5fe.

Files selected for processing: 18

  • quest/m0/README.md
  • quest/m0/clock-guard/README.md
  • quest/m0/clock-guard/js.md
  • quest/m0/clock-guard/rust.md
  • quest/m0/lite-passive-spin.md
  • quest/m0/test-flakes-2/README.md
  • quest/m0/test-flakes-2/copy-fill-idle.md
  • quest/m1/README.md
  • quest/m1/mock-clock-tests/README.md
  • quest/m1/mock-clock-tests/go-py.md
  • quest/m1/mock-clock-tests/js-net.md
  • quest/m1/mock-clock-tests/js-rest.md
  • quest/m1/mock-clock-tests/rs-bindings.md
  • quest/m1/mock-clock-tests/rs-media.md
  • quest/m1/mock-clock-tests/rs-relay.md
  • quest/m1/mock-clock-tests/rs-transport.md
  • quest/m1/retry-interval-forward.md
  • quest/m1/retry-interval-overload.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated kixelated left a comment

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.

Automated review by review (OpenAI)

Reviewed head: 631b5fe282582c7608239e452b03ae0d55117508.

No actionable findings in this planning-only change. I read all 18 changed documents and checked the key premises against the referenced source: the lite Live/Shared passive-reader loop, the real-clock copy_fill wait, JS timer cancellation and lint coverage, and the overload/refusal forwarding paths.

Direction: fixing the passive-reader state transition and enforcing deterministic tests at the shared clock layer addresses the causes rather than extending waits. The Rust guard remains a feasibility spike, as planned.

Limits: static GitHub review only. I did not execute tests, quest check, or the markdown checks, reproduce the reported flake, or independently recount the full audit. This is a review of the plans, not validation of their future implementations.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The lite spin plan parks on a None priority without waking when the last passive reader leaves, so the serve would stop burning a core but never cancel the upstream subscription. The rest are inventory corrections, inline.

Reviewed changes

  • Lite passive-reader spin — a new m0 quest plans to stop DatagramState::Live/Shared busy-looping when only passive readers remain, by tracking the last priority as Option<i8>.
  • lite-05 copy-fill flake — a new test-flakes-2 child plans to replace the 200 ms idle() wait with the file's Manual clock or an awaited event.
  • Retry intervals — two m1 quests plan to send #7993's may-pass interval on an over-limit PUBLISH refusal and to forward an upstream refusal's interval verbatim.
  • Clock guard epic — a new m0 epic plans a JS test preload that fails a test whose host-clock timer fires, a Rust spike of the same guard with a grep fallback, and restoring the mock-time rule to REVIEW.md.
  • Mock-clock burn-down — a new m1 epic with seven area children covering about 300 tests, plus the m0/m1 README entries for all of the above.

I re-ran both claimed checks at 631b5fe: quest check reports "605 documents ok" and sh/markdown.sh check is clean. The lite Live/Shared spin, the dropped upstream retry_interval, the EXCESSIVE_LOAD refusal, and the #7761 move of the mock-time rule all match the source.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

Comment on lines +29 to +31
- Track the last priority as `Option<i8>`, as the IETF subscriber does, so a
`None` priority parks on the waiter instead of re-polling. The fix lands
for both `Live` and `Shared`.

@pullfrog pullfrog Bot Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mechanism drops the close_unused() path. With only passive readers left the aggregate priority is None, so poll_priority(waiter, None) stays Pending when the last reader leaves and nothing re-checks whether the subscription is unused, which strands the upstream subscription rather than cancelling it — the goal's "or the readers go" half.

Technical details
# Lite `None` priority strands the subscription

## Affected sites
- `quest/m0/lite-passive-spin.md:29` — the plan tracks `Option<i8>` but names no wait for the consumer set emptying.
- `rs/moq-net/src/lite/subscriber.rs:2905` and `:2944` — today the loop only reaches `close_unused()` because `poll_priority(..)` returns `Ready(None)` on every pass.
- `rs/moq-net/src/model/datagram.rs:347-353` — `poll_priority` parks whenever `state.priority() == last`, and `None == None` never becomes false when the last passive consumer leaves.
- `rs/moq-net/src/model/datagram.rs:357-364` — `poll_unused` is ready once the priorities slab empties; nothing in `lite/subscriber.rs` polls it after setup (`:2980` returns `Pending` for `Live`/`Shared`).
- Reference: `rs/moq-net/src/ietf/subscriber/datagrams.rs:57` parks `Requested` on `request.poll_unused(waiter)` and reads `priority()` beside it.

## Required outcome
- With only passive readers, the serve parks without re-polling.
- When the last reader leaves, the track ends with `Error::Cancel`, as `close_unused()` does today.
- A demanding reader that joins still wakes the serve and its priority reaches the wire.

## Suggested approach
- Park on `producer.poll_unused(waiter)` while the tracked priority is `None`, and on `poll_priority(waiter, last)` while it is `Some`, so both wake sources survive the change. Update the plan bullet to name the `poll_unused` wait.

`States::recv_terminal`, which the scan undercounted.
- moq-cli, 9: `tests/integration/export.rs` 3, `auth_serve.rs` 2,
`listen.rs`, `src/archive.rs` 2, and `src/main.rs`.
- kio, 3: `src/clock/sim.rs` 2 and `src/channel.rs`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These three kio entries already meet the epic's goal: src/clock/sim.rs's tests run under sim::run and assert simulated instants, and src/channel.rs's until is a 20 s hang guard on real threads carrying an #[expect(clippy::disallowed_methods, reason = "real threads hang on real time, which no kio clock moves")]. There is nothing to convert, so drop the kio line. The same classifier pulls in other out-of-scope files below.

Technical details
# Inventories include tests the epic's scope excludes

## Affected sites
- `quest/m1/mock-clock-tests/rs-bindings.md:19` — kio: `src/clock/sim.rs` 2 are simulator tests (`rs/kio/src/clock/sim.rs:831`, `:848` run inside `run(async { .. })`), and `src/channel.rs` is the real-thread `until` hang guard (`rs/kio/src/channel.rs:512-516`).
- `quest/m1/mock-clock-tests/rs-transport.md:18-19` — moq-quic: `src/connection/mtud.rs` 4 and `src/tests/characterize/endpoint.rs` 2 are plain `#[test]`s passing explicit `Instant` arguments (e.g. `mtud.rs:544`, `:578`); they take `Instant::now()` as an arbitrary base and never wait.
- `quest/m1/mock-clock-tests/rs-media.md:16` — `src/capture/wgc.rs` and `src/frame/cuda_test.rs` drive a real device or encoder, which the same file's closing paragraph excludes.

## Required outcome
- Each area list names only real-clock negative proofs, settle sleeps, and elapsed-time assertions.
- Tests already on a simulated clock, explicit instants, real threads, or real devices leave the lists (or are marked as left alone).

## Suggested approach
- Re-derive the lists with the classifier restricted to the three agreed categories, then drop the entries above.


As of 2026-11-06, 28 tests: `tests/integration/auth_lifetime.rs` 9,
`session_revalidate.rs` 4, `public_rules.rs` 3, `goaway_cluster.rs` 2,
`runtime_uring.rs` 2, `tenant_matrix.rs` 2, `web.rs` 2, and one each in

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rs/moq-relay/tests/integration/ has no web.rs; the count looks like it came from src/web.rs, whose #[tokio::test]s build TLS configs with no timers (rs/moq-relay/src/web.rs:1700, :1789, the latter a real-socket test the epic exempts as real I/O). Fix the path and re-derive this entry's count.

each hop; leaving the lossy hop.
- ✅ m1: additive, no semver break.

Starts once #7993 lands. Cover it with a two-hop test in Rust, and in JS if

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#7993 merged into main as 0fe4df8d3 and is already in this PR's base, so this gate is satisfied and now reads as a blocker. quest/m1/retry-interval-overload.md:23 carries the same sentence; drop both, or point at the merged code.

@kixelated
kixelated added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 2c53d5b Nov 6, 2026
10 checks passed
@kixelated
kixelated deleted the quest-plan/follow-ups-1106 branch November 6, 2026 20:26
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