Repository navigation
quest: retain timer-only doubt over a fresh publisher's exemption - #8735
Conversation
Quest-Claim: 0c8dc5ad7da97ac5
The test drives real SRT sockets, so its jitter engine waits in real time. Under load that wait ran out, and KIO_DENY_HOST_TIMERS failed the test, as its sibling already declares with `kio::clock::host_time()`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The route model gains `Exempt`: "fresh" lets a relay move at once onto a doubted publisher's own route, offered on a session the publisher opened after the doubt began, and "own" and "session" each drop one condition. `Fresh.cfg` checks it beside its timer-only control `Reconnect.cfg`. The router runs it as the comparison rule `Rule::FRESH` in test builds only, and the route benchmarks compare it. It is safe in every bound checked, but frees only the publisher's own relay: the 24-relay reconnect cases end 25 to 75 ms sooner, a publisher that moves gains nothing, and churn sends 55% more route messages. The quest now waits on the maintainer's call, recommending timer-only doubt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
ℹ️ No critical issues. One small guard for the model is suggested inline.
Reviewed changes
This run reviewed the full PR: a test-only doubt exemption for a reconnecting publisher in path::Router, the matching Exempt comparison in RouteHandoff.tla, and the documents that record the measurements.
- Router exemption (
Rule::FRESH):Router::barredskips doubt for a path that names only the peer, when the peer's session number is abovesince, theopenedcount saved when the doubt began.sinceis set only when a node goes from not doubted to doubted, so later DEADs do not reset it, and it is removed whentimeoutfires the doubt. Both formerdoubts_pathcallers,selectandhopes, now go throughbarred. - Shipped behavior unchanged: outside
#[cfg(test)], the only change moves the deadline update intoRouter::suspect, whichfailand the GOAWAY path now share. This is a pure refactor. - Model:
ExemptaddsOwn,Exempted, andBarred, plus a per-nodefreshset.Connectsets it throughOpened,Closedclears it, andLiftclears it under"fresh", which matches the router'ssincelifecycle. Every existing config pinsExempt = "none".Reconnect.cfgis the control forFresh.cfg. - Benchmarks:
COMPAREDaddsRule::FRESH.RULES, which the merge-gate tests use, is unchanged, andsh/gh/nightly.shdoes not depend on rule names. - Docs and quest: the
verify-routeskill's sync commit moves to12537d1c2(#8622's squash; no file inroute/path.rsorroute/reader.rschanged after it). The skill gains a coverage row forbarred,fresh, andsuspect(sh/coverage-tables.pypasses). The quest is rewritten as the maintainer's adopt-or-retain call. moq-srttest fix:a_publisher_marker_keeps_an_srt_receiver_playingnow declareskio::clock::host_time()for its real SRT sockets, asrs/AGENTS.mdrequires.
claude-opus-5-5 | 𝕏
|
No actionable comments were generated in the recent review. WalkthroughThe routing changes add a test-only exemption for a doubted neighbour’s own route when it is offered on a session opened after doubt began. The formal model defines fresh, own, and session exemption rules. Model configurations and router benchmarks cover the fresh rule. Documentation reports model-checking and router-test results and records a pending adoption decision. The SRT discontinuity test switches to the host clock before using real sockets. Merge Risk:
|
|
Heads-up from #8734, which works on flapping-publisher: it deletes Your measurement shaped #8734's rule. I measured the broad origin exemption on the same churn script: compared with timer-only doubt, it sent 160% more route messages, sent 189% more claims, and took 155% more claims offline. #8734 therefore keeps the timer for a link that returns during the doubt. It spares only a dual-homed publisher's other links and a publisher that moves. On your one-home script, that rule matches timer-only doubt within 3% (9,049 vs 8,785 route messages, 3,452 vs 3,336 claims offline). With two homes, it has 14% less time unrouted, for 22% more route messages and 3% more claims offline. The decision and both tables are in #8734's body. (Written by Claude Opus 5.5) |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
This run reviewed the one commit added since the prior Pullfrog review.
- Rejected unknown
Exemptvalues:RouteHandoff.tlagainsASSUME Exempt \in {"none", "fresh", "own", "session"}after theCONSTANTSblock, so a misspelled value fails at TLC startup instead of silently acting as"none". This addresses the earlier inline suggestion.
claude-sonnet-5-5 | 𝕏
The maintainer declined the exemption on 2026-11-14. Rule::FRESH and the model's Exempt stay as comparisons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the delta since 3c47bcf is docs and quest bookkeeping only.
- Recorded the decision — the
verify-routeskill and epic README now say the maintainer declined the fresh-publisher exemption on 2026-11-14, linking #8735. - Deleted the quest —
fresh-publisher.mdis removed, matching the retained timer-only doubt. The only remaining mention is plain code formatting inflapping-publisher.md, which #8734 rewrites.
claude-sonnet-5-5 | 𝕏
The maintainer retained timer-only doubt, so the comparison rule goes: Rule::FRESH, Doubt::fresh and its `since` bookkeeping, the model's Exempt variant with Fresh.cfg and its control Reconnect.cfg, and the benchmark rows. The router, model, skill, and benchmarks take main's versions; the results stay in epic Plan item 27. 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>
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the delta since a8625e4 trims the PR to its final shape.
- Dropped the declined exemption's code —
Doubt::fresh,since,barred,Rule::FRESH, theExemptmodel constant,Fresh.cfg,Reconnect.cfg, and theverify-routeskill edits are gone, soroute/path.rsandRouteHandoff.tlamatchmain. No dangling references remain. - Recorded the results in item 27 —
quest/m2/cluster-routes/README.mdnow holds the declined-decision paragraph and the measurements table beside the existing measurements. - Kept the
moq-srtfix — the test declareskio::clock::host_time()for its real sockets.
claude-sonnet-5-5 | 𝕏
- quest/m2/cluster-routes: main declined the fresh publisher's exemption (#8735) and deleted its quest, so its link to the switch goes with it; item 28 keeps main's text with the switch linked to its PR. - `waits_out_the_doubt` writes its groups as #8638's start-addressed API does (`append_next`, `at_start`, `close`), as the other route-change tests now do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Finishes quest/m2/cluster-routes/fresh-publisher.md: the maintainer retained timer-only doubt, so this PR deletes the quest and keeps its measurements in the epic's Plan item 27.
Problem
Under the path rule (moq-dev/moq#8622), a publisher that reconnects waits out the one-second doubt its own closed session cast: 1,050 ms in a 24-relay clique and 1,875 ms in a ring, against 50 ms and 325 ms for the distance rule. The quest asked whether a narrow exemption is safe, and whether it actually shortens those waits: a relay moves at once onto the publisher's own route, offered on an authenticated session the publisher opened after the doubt began.
Approach
RouteHandoff.tla(Exempt,Fresh.cfgbeside its timer-only controlReconnect.cfg) and ran it in the router as the test-only comparison ruleRule::FRESH(Doubt::fresh,since). The maintainer retained timer-only doubt, then picked trimming that code: moq-dev/moq#8764 slatedRouteHandoff.tlafor deletion, so nobody would rerun it, and git keeps it. The router, model,verify-routeskill, and benchmarks now match main.moq-srt'sa_publisher_marker_keeps_an_srt_receiver_playingdrives real SRT sockets but never declaredkio::clock::host_time(). Under load, its jitter wait ran out on the host clock andKIO_DENY_HOST_TIMERSfailed the test (seen injust check; it passes alone).Results
The exemption is safe in every bound checked, but it frees only the publisher's own relay. Every other relay still doubts the publisher and refuses the route forwarded to it.
24-relay reconnect cases (
benchmark_route_rules;rightis until every relay routes to the publisher;darkcounts relay-rounds with no route while a path exists):Churn (
benchmark_route_churn, 400 cases, the publisher's link flapping most):The exemption cuts dark observations by 28%, but costs 55% more route messages, 85% more claims sent, and 80% more claims seen going offline. Timer-only doubt damps a flapping publisher; the exemption shows each flap.
Decisions
moq-srtfix; dropRule::FRESH/Doubt::fresh,since,Exempt,Fresh.cfg, and the benchmark rows.The rest were taken by the agent under /quest-afk, for the maintainer's review.
Router::suspectis dropped too. It only deduplicated the deadline update indrainsandfail, and moq-dev/moq#8734, landing in parallel, adds its ownsuspectthat does the same and also records when the doubt began. Keeping this one would only add a conflict with it. Rejected: keeping it on its own.Reconnect.cfggoes withFresh.cfg. It existed as the exemption's control and setExempt. Rejected: keeping it as a timer-only triangle, in a model slated for deletion.flapping-publisher.mdkeeps its code-formatted mention offresh-publisher.md. feat(route): a dual-homed publisher's other links survive doubt about it #8734 rewrites that paragraph, so editing it here would conflict. Rejected: editing it here."own"control).moq-srtflake is fixed here, since it failed this branch'sjust check. No open PR fixed it.Impact
moq-srttest.fresh-publisher.mdand edits item 27's decision line identically, so whichever lands second keeps feat(route): a dual-homed publisher's other links survive doubt about it #8734's inserted decision beside this table. lite-07 sessions run the path router #8731 modifiesfresh-publisher.md, which both this PR and feat(route): a dual-homed publisher's other links survive doubt about it #8734 delete.Alternatives
"own"): it would catch the move case, but it still frees only the relay the publisher is attached to."session"): rejected by the quest. A new transport session does not show the origin is alive.Follow-ups
None.
Validation
just checkpasses (321 Rust tests in the changed crates, every module),quest checkpasses (512 documents), and trial merges with lite-07 sessions run the path router #8731's and feat(route): a dual-homed publisher's other links survive doubt about it #8734's heads show only the conflicts listed under Impact.tla2tools.jarand Community Modules, throughsh/tlc.py). Triangle, exhaustive, every invariant includingNoStaleReturn:"none"1,490,147 distinct states,"fresh"1,822,789,"own"1,750,688,"session"2,909,134, no error. Kite (a third relay behind relays 1 and 2): all four variants, the control included, breakNoStaleReturnthrough two closes and no reconnect,Twice.cfg's accepted case; without that invariant,"fresh"showed no violation in 12.9M distinct states, every ordering to depth 14, before a 45-minute cap.Rule::FRESH: all 42both!fixtures,sweep_handoff,sweep_scoped_handoff,sweep_requests_reach_a_quiet_origin, and the residual fixtures passed atMOQ_ROUTE_SCENARIOS=2000;benchmark_route_rulesandbenchmark_route_churnproduced the tables above.just checkandjust cipassed: 13,134 tests.Caused by
a_publisher_marker_keeps_an_srt_receiver_playingModels
Size: L
Implementer: Claude Code, claude-opus-5-5, unknown effort
Implementer: Claude Code, claude-opus-5-5, low effort (iterate, XS)
Implementer: Claude Code, claude-opus-5-5, unknown effort (iterate, S)
Review
Rewritten by
quest reviewafter each round.Head: 5722a22
Base: origin/main at 98ed0f8
Risk: medium,
the base has no RISK.md to name what is always high or customer-facing, so every change is mediumClassified: medium by default
Reviewer: Codex, gpt-6.1-sol, xhigh effort
Session:
01a1de2f-1db8-7581-9b35-113e9beca0bf, round 1Score: 9/10,
No actionable defects remain; the trimmed diff preserves results and appropriately enables host time for the real-socket test, though builds and tests were not run.Rounds: 4; findings 2 (low 2): fixed 1, refuted 1, open 0
Findings: none
The last round's findings, at a8625e4:
quest/m2/cluster-routes/flapping-publisher.md:41:Update the remaining reference to the deleted quest;The author's refutation is supported by 154bb658c:quest/m2/cluster-routes/flapping-publisher.md:12–27, where the rewrite removes the obsolete quest reference.🤖 Generated with Claude Code
(Written by Claude Opus 5.5)