fix(sync): harden canonical recovery

This commit is contained in:
Timo
2026-08-25 21:21:18 +02:00
parent a65fe2f576
commit def5e1a380
10 changed files with 363 additions and 46 deletions
+12 -6
View File
@@ -44,17 +44,23 @@ the optional `media-state-v1` capability. A joining/reconnecting client validate
the room/revision, respects Host Control solo mode and Episode Lobby, then sends an
internal `APPLY_CANONICAL_MEDIA_STATE` message to the existing content/video path.
That path reuses frame election, Netflix/Disney page-API seeks, native play/pause,
the 2-second drift tolerance, and programmatic-event suppression. The apply is
one-shot recovery: a pending playing snapshot advances from its local receipt
time while waiting for a target, and the apply creates no action history,
notification, command ACK, or relay media event.
the 2-second drift tolerance, and programmatic-event suppression. Recovery only
completes after playback state and position verification. Transient failures
retry after 250, 750, 1500, and 3000 ms, while target, heartbeat, and content-boot
signals can retrigger a pending attempt within that bound. A pending playing
snapshot advances from its local receipt time while waiting for a target, and
the apply creates no action history, notification, command ACK, or relay media
event.
Force Sync remains a two-phase ACK protocol. A valid `PREPARE` is temporary
room-wide choreography; the next authorized `EXECUTE` commits the latest target
visible to peers to canonical state before the shared Force Sync timeout. The
visible to peers to canonical state before the relay target TTL. That TTL is
longer than the client ACK timeout so its scheduled fallback can still land. The
offline queue replays an adjacent `PREPARE`/`EXECUTE` pair in one paced batch and
retains both if delivery fails. Per-sender
`seq`, peer heartbeats, and the reconnect queue remain separate mechanisms.
`seq`, peer heartbeats, and the reconnect queue remain separate mechanisms. The
relay rejects duplicate/regressing current-client media sequences before they
can diverge canonical truth from live receivers.
## 3.2 Offline Media Intent
+18 -10
View File
@@ -121,21 +121,28 @@ Only accepted, sanitized room controls update canonical state:
established playback state.
- a valid `force_sync_prepare` records only temporary coordination state. The
next authorized `force_sync_execute` commits the latest room-wide prepared
target as playing before `FORCE_SYNC_TIMEOUT` expires. Expired targets are
cleared and cannot alter canonical state. The latest valid prepare is also
the only post-demotion execute exemption in Host Control mode.
target as playing before `FORCE_SYNC_TARGET_TTL` expires. This relay TTL is
intentionally longer than the client's `FORCE_SYNC_TIMEOUT` ACK wait so the
normal timeout fallback remains deliverable. Expired targets are cleared and
cannot alter canonical state. The latest valid prepare is also the only
post-demotion execute exemption in Host Control mode.
`peer_status` heartbeats are observations and never rewrite canonical intent.
Per-sender `seq` still orders commands from one sender; canonical `revision`
orders server-accepted room transitions. Neither replaces the other.
For current clients, the relay drops invalid, duplicate, or regressing `seq`
values on room-moving media commands before relay/canonical mutation. Legacy
clients without `seq` retain their existing behavior. Canonical `revision`
orders server-accepted room transitions; it does not replace per-sender order.
On join/reconnect, a capable extension applies a valid snapshot once through an
extension-internal recovery message. Existing seek/page-API and native-event
On join/reconnect, a capable extension attempts to apply a valid snapshot
through an extension-internal recovery message. Recovery is only marked handled
after playback state and position verification. Transient failures use bounded
retries after 250, 750, 1500, and 3000 ms and can also be retriggered by target,
heartbeat, or content-boot signals. Existing seek/page-API and native-event
suppression prevent `play`, `pause`, or `seek` echoes. Pending recovery is scoped
to the room/revision in `chrome.storage.session`, waits for the selected media
target lifecycle, and projects a still-playing snapshot from its local receipt
time before a delayed apply. It is cleared on leave/switch. Intentional host-only guest
desync and an active Episode Lobby take precedence over snapshot recovery.
time before a delayed apply. It is cleared on leave/switch. Intentional host-only
guest desync and an active Episode Lobby take precedence over snapshot recovery.
Compatibility is additive: new clients use old behavior with a relay that omits
the capability; old clients ignore the extra `room_data` field from a new relay.
@@ -341,7 +348,8 @@ the latest validated room target retained from `force_sync_prepare`. In
`host-only` mode, only controllers may send it.
The relay also allows that latest valid initiator's execute event after their
controller state changed before execute. Invalid prepares are dropped and grant
no exemption.
no exemption. The retained target expires after `FORCE_SYNC_TARGET_TTL`, which
includes a relay grace period beyond the client's ACK timeout.
## Episode Lobby