Skip to content

#725 Accept a respawned core's roster instead of keeping the dead core's - #727

Merged
iamfatness merged 2 commits into
mainfrom
fix/725-roster-dip-source
Oct 1, 2026
Merged

iamfatness merged 2 commits into
mainfrom
fix/725-roster-dip-source

Conversation

@iamfatness

Copy link
Copy Markdown
Owner

Root cause and fix for #725 (multiview second row reflowing on every Take). #726 was the mitigation; this removes the cause.

Root cause

After the supervisor respawns the media core, the shell keeps the dead core's Zoom roster for the rest of the app session.

  • The roster epoch is process:meeting:token. A new core process starts again at 1:1; only the diagnostic token differs.
  • ZoomRosterSnapshotPolicy.Accept orders epochs by (process, meeting), so the new core's roster is never "newer" and is rejected.
  • That policy relies on "a core restart clears the bridge snapshot". StopCore does. The supervisor's crash-respawn path did not.
  • A Take applies its own raw sync response, which carries the true roster. The next bridge snapshot puts the stale one back. That alternation is the dip.

Evidence

  • Owner session, 2026-10-01, beta e9a559f: media-core.log shows the core rejoining the meeting at 07:25:29, 07:27:11 and 07:27:43. The 42 mv-roster-miss lines start at 07:31:26 and line up one-to-one with [take] lines.
  • Reproduced in the test meeting: join, kill corevideo-native.exe, wait for the respawn, Take. Every Take logged a drop:
    roster-drop: pid=16795648 origin=meeting-fields prevN=9 newN=8 ... snap(epoch=1:1:18772-0 rev=38 n=8) bridge(epoch=1:1:18772-0 rev=38 n=8) while /snapshot (the core's own JSON) read epoch=1:1:23540-0 rev=11 n=9.
  • Same sequence on this branch: the shell follows the new core (slot 9 picks up the producer's new id by roster-sync), and 12 Takes log zero dips. The one roster-drop in that run was a real leave: the killed engine's old participant timing out of the meeting, same epoch on both sides.

Change

  • MediaCoreBridgeService.ReleaseRosterBarrierForRespawnedCore: at the respawned core's handshake, clear the roster epoch and revision and reset roster recovery. Keyed on the supervisor restart count, so an ordinary handshake does nothing. Participants are kept until the rejoin replaces them, so slots are not freed during the gap.
  • roster-drop log line: whenever a participant leaves the shell's roster, name the apply path and the roster epoch it read next to the bridge's. Two epochs is this defect; one is a real leave.
  • mv-roster-miss: no longer fires for the empty pre-join roster (ten false "dropped" lines on every launch).
  • CLAUDE.md Multiview second row reflows on Take: an in-show camera-off participant briefly drops from the shell roster #725 entry rewritten with the cause.

Tests

  • New RosterAfterCoreRespawnTests: a fake core that dies on command and comes back on a same-numbered epoch at a lower revision. Fails without the fix (Expected "1:1:core-b", Actual "1:1:core-a").
  • New MultiviewParticipantGraceTests.AnEmptyRosterBeforeAMeetingLogsNothing.
  • Full suites: MediaCore 2271/0, WinUI 1598/0, Control 84/0.

Not covered

  • The fix was verified live with a killed core, not with whatever made the core restart in the owner's session. Why it restarted three times at 07:25 to 07:27 is not investigated here.
  • The 1 s grace from #725 Hold in-show multiview slots through a one-apply roster dip #726 stays. It is no longer load-bearing for this case.
  • Native code is untouched; no native build or test run.

🤖 Generated with Claude Code

A core the supervisor respawns starts its roster epoch again at the same
process and meeting numbers as the one that died; only the diagnostic
token differs. ZoomRosterSnapshotPolicy could not order it after the
installed barrier, so the bridge kept the dead core's roster for the rest
of the app session. A Take applies its own raw sync response, which had
the true roster, and the next bridge snapshot restored the stale one.
That alternation was the multiview reflow on every cut.

- MediaCoreBridgeService drops the roster barrier at the respawned
  core's handshake, keyed on the supervisor restart count. Participants
  are kept until the rejoin replaces them.
- roster-drop log line: names the apply path and both roster epochs
  whenever someone leaves the shell's roster.
- mv-roster-miss no longer fires for the empty pre-join roster.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iamfatness
iamfatness merged commit 17c5b53 into main Oct 1, 2026
34 of 35 checks passed
@iamfatness
iamfatness deleted the fix/725-roster-dip-source branch October 1, 2026 23:45
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.

2 participants