Repository navigation
quest: plan lite-07's hop list on routes, one routing stream per side, and its interest set - #8495
Conversation
Records the 2026-11-10 /quest-plan on lite-07 routing: every publisher stays an origin node, holds and merged floors stay, and each side says everything about routing on one unidirectional Announce Stream whose interest is a set of prefixes edited in place. Splits the work into the interest set (model and router) and the route stream (wire and sessions), narrows echo-order to its model half, and plans the seqno bump fix, two offline-not-END fixes, the browser mesh, and core and edge roles in the mesh overlay. Co-Authored-By: Claude <noreply@anthropic.com>
The route model still counts to infinity with the echo on the neighbour's FIFO once the origin sits behind a relay. Records the decision (epic item 25), grows echo-order to own the sound drain in the model, Sim, and router, and keeps per-link costs and that shape in the mesh model. Co-Authored-By: Claude <noreply@anthropic.com>
Pre-merge checks:
|
There was a problem hiding this comment.
ℹ️ No critical issues — two plan-accuracy nits inline, one findability note below.
Reviewed changes
- One routing stream per side — item 21 and
routes/route-stream.mdreplace the per-request stream split with one unidirectional Announce stream per endpoint, its interest a prefix set, an echo type of its own because both sides' Route IDs then share an ID space. - Interest as an edited prefix set — items 22-24 and
routes/interest-set.mdsettle per-prefix floors and SYNCED, non-nesting sets, and swaps that re-send nothing both sets cover, superseding items 16 and 17. - A sound route drain — item 25 and
routes/echo-order.mdreplace the per-neighbour echo with one that waits for the echoer's own downstream, carrying the origin-behind-a-relay counterexample that motivated it. - Seqno bumps and two offline misreports —
m1/route-seqno-bumps.mdnarrows the unprompted bump to a route-link change, andm1/offline-cursor-fallback.md/m1/offline-js-handle.mdown the two#8375findings where a lost route reads as END. - Roles and browser transit —
m3/mesh/overlay.mdgains core and edge roles with product link pricing, andm5/browser-mesh.mdplans forwarding tabs behind the Rust-to-TypeScript port, withm4/p2p/README.mdandroutes/js.mdupdated to match.
The code references, test names, config names, and wire limits the plans cite all check out against this tree (including the Gone::End capture-change arm, the two router.bump() sites, the residual_an_overtaking_echo_counts_to_infinity 3→8 climb, and MAX_MESSAGE_SIZE).
ℹ️ Four new quests are missing from their milestone indexes
quest/m1/README.md lists 24 of its 25 children and quest/m5/README.md lists every one, so the new offline-cursor-fallback, offline-js-handle, route-seqno-bumps, and browser-mesh quests are the only gaps — and the two offline quests are linked from nowhere except each other. quest check accepts unlisted children, so this is findability only, but if the milestone ## Required list is the priority index, four one-line entries would keep it complete.
Technical details
# Missing milestone index entries
## Affected sites
- `quest/m1/README.md` — no entry for `offline-cursor-fallback.md`, `offline-js-handle.md`, or `route-seqno-bumps.md`
- `quest/m5/README.md` — no entry for `browser-mesh.md`
## Required outcome
- Each new child appears in its milestone README's `## Required` list, matching how every other child of m1 and m5 is indexed.deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
The route model ran the deferred echo: it fails when a session closes before answering. A hop list per origin route passes the same shapes, with the echo ordered or not, so the fix lands on today's streams. Co-Authored-By: Claude <noreply@anthropic.com>
…nce-stream # Conflicts: # quest/m2/cluster-routing/routes/README.md
… browser mesh Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Important
The hop list's own-ID check has a scoped-session hole the model does not cover, and two spec-text updates are missing from the work list.
Reviewed changes
The fix changed from a deferred echo to a hop list since the prior review:
- A hop list per origin route — item 25 and the retitled
echo-order.mdnow make each route name the relays it crossed, with a node never using one that names itself; the deferred echo is recorded as run and superseded, including the two-flap failure that killed it. - The model evidence is written down —
echo-order.mdcitesDRAIN.mdonroute-drain-counterexamples(5e7e3eb43) for every shape, bound, and stopped run, and gates the list's unmeasured message cost on #8479's announce budget before touching the router. - Downstream plans moved with it —
interest-set.mdstarts from the hop list before moving the echo onto one FIFO,route-stream.mdrecords that the fix no longer depends on it,js.mddecodes the list, and the epic's loop-freedom bullet states the rule. - Main merged in — the merge brings
529fa6ac9up to date.
I checked the new claims against the reference branch and the tree: the DRAIN.md state counts, bounds, and "not run" list match the plan, 5e7e3eb43 is that branch's head, CountToInfinity.cfg is the open-finding config with the origin as a triangle node, MAX_HOPS is 32, and the model's origin does ignore routes for its own identity.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues in
be4049e5. The two open threads from the first review are addressed cleanly, and approval is withheld only because the three findings from the previous review are still open.
Reviewed changes
- JS's entry point named correctly —
route-stream.mdandjs.mdnow sayroutes()in JS andannounced()in Rust, in both the goal and the code-replacement list. - Neighbour-key ownership settled —
interest-set.mdnow states thatroute::Nodekeeps registering each announce stream under its own key until the route stream lands, and that only the router gains the one-key ability here (used bySimand the tests), so "changes no behaviour on its own" holds. - The route stream picks it up —
route-stream.mdnow says the same change is what makes the sessions register both directions under that key. - Browser mesh indexed —
quest/m5/README.mdlistsbrowser-mesh.mdin## Required.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
…pdate Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
ℹ️ No critical issues — one accuracy note on the final work item.
Reviewed changes
Since be4049e5 the delta is one hunk in routes/echo-order.md, settling the scoped-grant hole and the two spec-text gaps from the last review:
- Hop-list entries are never qualified — a relay still files a scoped peer's origin under a qualified key, but forwards list entries as received, since no relay can derive another's qualification. A scoped peer can then only edit lists on routes filed under its own qualified identities, and a lap through it is still refused by the requalify rule, to be covered by a
Simcase because the model has no scoped grants. - The draft's Node ID Parameter joins the work list — a node that only forwards must now declare a Node ID, which the SETUP text currently contradicts.
- The handoff contract's drain bullet is named — it joins the loop-freedom bullet and decision 6 as an epic site to update.
ℹ️ The epic-update list names the wrong bullet
echo-order.md:145-149 names the loop-freedom bullet, decision 6, and the drain bullet. The loop-freedom bullet (quest/m2/cluster-routing/routes/README.md:190-198) already states the hop-list rule after 7daa3267, while the fresh-identity bullet (README.md:145-151) still argues from the echo bound: keeping the feasibility distance until every ROUTE_END is echoed leaves a collected incarnation reappearing only from an in-flight message that delivers the retraction next, which is exactly what the origin-behind-a-relay case refutes. Name that bullet instead so the implementer updates a site that still carries the refuted claim.
Technical details
# Epic sites that still rest on echo-alone safety
## Affected sites
- `quest/m2/cluster-routing/routes/echo-order.md:145-149` — update list names the loop-freedom bullet
- `quest/m2/cluster-routing/routes/README.md:190-198` — loop-freedom bullet, already rewritten to the hop list in `7daa3267`
- `quest/m2/cluster-routing/routes/README.md:145-151` — fresh-identity bullet, still grounds reappearance in the retained distance and the echo
- `quest/m2/cluster-routing/routes/README.md:387-394` — drain bullet, correctly named ("as safe as a fresh node joining")
## Required outcome
- The work item names every epic site that still carries the echo-alone justification, so `README.md:145-151` is not left arguing the premise the model refuted.
## Open questions for the human (optional)
- The loop-freedom bullet may still want a rewording pass; it already states the rule today.deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

Problem
lite-07's ROUTE_END echo can overtake a route, and two relays then count to infinity (moq-dev/moq#8447). The settled fix, the echo behind the echoer's routes, assumes one stream carries those routes, but a session may hold several Announce Streams each way, so moq-dev/moq#8487 could fix only the model. This /quest-plan reopened the route layer's rules to settle that.
It also found that the reordered echo is not enough. With an origin behind a relay, the model still counts to infinity: a derived route held by a third relay comes back after every neighbour has echoed.
Approach
Quest files only. Decisions are recorded in the quests' Plans; in brief, as items 18 to 25 of the routes epic:
The hop list replaced an earlier decision in this same plan, a relay echoing a ROUTE_END only once its own downstream had. The route model ran both. The branch
route-drain-counterexamples(5e7e3eb434) holds the model with each variant behind a switch, per-link costs, the configs, fourSimreplays, and every result inrs/moq-net/verify/route/DRAIN.md:Not run: full bounds on the 4-relay shapes, any 5-relay shape, and four runs stopped at 8 to 21 million states with no violation. The list's message cost is unmeasured; the quest measures it against moq-dev/moq#8479's announce budget before touching the router.
New quests:
quest/m2/cluster-routing/routes/interest-set.md[L]: the edit rule proven in the TLA+ model and implemented in the router, no wire.quest/m2/cluster-routing/routes/route-stream.md[XL]: the wire, the sessions, the draft, and js/net.quest/m1/route-seqno-bumps.md[S]: today every announce stream open and close bumps a relay's seqno.quest/m1/offline-cursor-fallback.md[S] andquest/m1/offline-js-handle.md[S]: the two unowned review findings on moq-dev/moq#8375, where a lost route reads as END.quest/m5/browser-mesh.md[L]: tabs that forward for LAN peers, on the Rust-to-TypeScript port.Edited quests:
echo-order.mdis rewritten and retitled "A node never takes back a route that crossed it": the hop list in the model,Sim, the router, the wire, and the draft, on today's Announce Streams.js.md, the P2P epic, andquest/m3/mesh/overlay.md, which gains core and edge roles in place of the non-forwarding node.Impact
None. Quest files only.
Alternatives
Choices made without asking
For review; each is marked a recommendation in the quest it sits in.
upstream.route-drain-counterexampleswas pushed to keep the model variants and replays; delete it once echo-order's PR carries them.Follow-ups
Sim. That is no longer this quest's fix; its session should mergemainafter this lands and start the hop list frommain's model.quest/m1/route-mesh-model.mdas finished. This PR no longer edits that file; echo-order turns the count-to-infinity rule on in test(net): the route model checks an announce budget and convergence on meshes with clients #8479's meshes and adds link costs to them.Models
Size: M
Implementer: Claude Code, claude-fable-5-2, high effort
Review
Rewritten by
quest reviewafter each round.01a1c8f4-9345-7b80-b9a7-9a7654913282, round 4The incremental planning changes clarify hop-list identity handling and required verification consistently with the existing qualification rules, with no actionable defects.Checks on this head:
quest check --base origin/mainpassed (524 documents). No other local check was run after the merge ofmain; the diff is quest files only.🤖 Generated with Claude Code
(Written by Claude Fable 5 with high)