feat(contracts): add ReplayGap signal to MxEvent for reconnect replay

This commit is contained in:
Joseph Doherty
2026-06-16 05:04:17 -04:00
parent 056bb39a4d
commit 9beb67c1e9
2 changed files with 592 additions and 245 deletions
File diff suppressed because it is too large Load Diff
@@ -715,6 +715,20 @@ message MxEvent {
google.protobuf.Timestamp gateway_receive_timestamp = 11;
optional int32 hresult = 12;
string raw_status = 13;
// Gateway-synthesized reconnect-replay gap signal. Set ONLY on the single
// sentinel MxEvent the gateway emits at the head of a StreamEvents stream
// that was resumed via StreamEventsRequest.after_worker_sequence when the
// requested sequence is older than the oldest event still retained in the
// session replay ring (i.e. events were evicted and cannot be replayed).
// On that sentinel, `family` is UNSPECIFIED, the `body` oneof is unset, and
// no per-item fields (server_handle/item_handle/value/...) are populated;
// clients MUST treat a present `replay_gap` as "you missed events — discard
// local state and re-snapshot" and read `requested_after_sequence` /
// `oldest_available_sequence` from it. Unset on every normal MXAccess event.
// Additive (proto3): existing clients that ignore this field continue to
// deserialize the stream unchanged. (Reconnect/replay logic is Task 12; this
// is the contract surface only.)
optional ReplayGap replay_gap = 14;
oneof body {
OnDataChangeEvent on_data_change = 20;
@@ -726,6 +740,22 @@ message MxEvent {
}
}
// Reconnect-replay gap signal carried by a sentinel MxEvent (MxEvent.replay_gap)
// when a client resumes StreamEvents via after_worker_sequence but the requested
// sequence predates the oldest event still held in the session replay ring.
// The events in the open interval (requested_after_sequence, oldest_available_sequence)
// were evicted from the ring and cannot be replayed, so the client must
// re-snapshot rather than assume a contiguous event history.
message ReplayGap {
// The worker_sequence the client asked to resume after
// (StreamEventsRequest.after_worker_sequence).
uint64 requested_after_sequence = 1;
// The oldest worker_sequence still retained in the replay ring. Events with
// worker_sequence in (requested_after_sequence, oldest_available_sequence)
// were evicted and are unrecoverable.
uint64 oldest_available_sequence = 2;
}
enum MxEventFamily {
MX_EVENT_FAMILY_UNSPECIFIED = 0;
MX_EVENT_FAMILY_ON_DATA_CHANGE = 1;