Skip to content

fix(net): record the placeholder-rewrite decline like every other - #3091

Merged
kixelated merged 3 commits into
devfrom
claude/announce-decline-record
Aug 27, 2026
Merged

fix(net): record the placeholder-rewrite decline like every other#3091
kixelated merged 3 commits into
devfrom
claude/announce-decline-record

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

A regression that landed on dev when #3066 squashed onto a base that already carried #3065. Neither side conflicted, so nothing flagged it.

What broke

#3065 made every path out of start_announce that declines an announce record it first, via announced.declined(path). That is the whole of the #3050 fix: a declined announce keeps its path, so a later ANNOUNCE_START for it is reported rather than accepted, and the declined one's ANNOUNCE_END retires nothing instead of a stranger's route.

#3066 added a decline path that #3065 never saw, where rewriting a Lite03 placeholder hop would name this session twice. It was written against a base without the record, so it just returns:

if self.version_lacks_hops() && hops.replace_first(crate::Origin::UNKNOWN, self.session_origin).is_err() {
    tracing::debug!(broadcast = %self.log_path(&path), "dropping announce reflected by its session");
    return Ok(false);
}

On dev today that leaves five ways to decline an announce and four that record it. git merge-tree reports the merge clean, both branches compile, and every existing test passes, because the invariant lives across sibling call sites rather than in anything a merge can see.

The fix

Record it like the other four. The mechanical half is one line.

The rest is every_declined_announce_is_recorded, which walks the path that was missed: a chain the placeholder rewrite cannot take because this session's id is already in it, then a second ANNOUNCE_START for the same path, which must be Error::ProtocolViolation. Removing the declined call fails it with "a declined announce still holds its path, so a second start for it is a violation".

That converts the rule from something a reviewer has to notice into something CI enforces, which is the actual point: the decline list is exactly the kind of thing the next edit falls off the end of, and the failure is silent.

Verification

just check clean; 3277 Rust tests and 590 @moq/net tests pass. cargo check -p moq-ffi -p libmoq clean too, since just check never builds those.

🤖 Generated with Claude Code

(written by Claude Opus 5)

Rebasing onto a dev that carries #3065 merged cleanly and still lost an
invariant. That PR made every path out of `start_announce` that declines an
announce record it first, so a declined announce keeps its path and a later
`ANNOUNCE_END` retires nothing rather than a stranger's route. This branch adds a
decline path #3065 never saw, where rewriting a placeholder hop would name this
session twice, and git took it verbatim: five returns, four records, no conflict
and no failing test.

Record it too, and pin the rule with a test that walks that path, so the next
edit to the list fails instead of merging quietly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: acd9417a0a

ℹ️ 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".

Comment thread rs/moq-net/src/lite/subscriber.rs Outdated
kixelated and others added 2 commits August 27, 2026 07:57
…cline

The rule that a declined announce keeps its path was spread across five sibling
branches of `start_announce`, each responsible for calling `declined` before
returning. That is what let the merge lose one: nothing about a new early return
makes the omission visible.

Take the record once, immediately after the duplicate check, and let acceptance
replace it. Every way out then leaves it standing by construction, and the next
early return cannot free a path the peer still holds. `restart_announce` keeps
`declined`, where the point is finishing a route that is already attached.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`reserve` overwrites whatever is at the path, which is only safe because the
duplicate check just established there is nothing there. Say so, and assert it,
so a later caller cannot drop an attached route's source without finishing it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@kixelated
kixelated enabled auto-merge (squash) August 27, 2026 15:18
@kixelated
kixelated merged commit 7e517dd into dev Aug 27, 2026
3 checks passed
@kixelated
kixelated deleted the claude/announce-decline-record branch August 27, 2026 15:25
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