WindowExpired
The reconnect window for peerId expired at epoch-millis at without a valid resume.
detectedAt names which partition episode expired, on exactly the terms WindowOpened.detectedAt does: the at this controller was handed in JoinerReconnectController.onPeerUnresponsive for the drop that opened this window, echoed back unchanged. Never a clock read at the emit site — that would answer when the expiry happened, which every receiver already has as at.
Why the identity matters more here than on WindowOpened, not less (#2556). A receiver without it can only ask whether the peer is partitioned now — the guard #1781 already proved insufficient for the announcement's reversible effect, moving a deadline. What a WindowExpired drives is not reversible: the room fans out an authoritative Farewell and evicts the seat, and there is no re-admit path behind it. So a late expiry for an episode the peer already recovered from — arriving while it is partitioned again in a later episode — would pass a liveness-only guard and take a seat whose window has not run out. Identity is what rejects it.
Required and non-nullable for WindowOpened.detectedAt's reason: a nullable field lets every existing emitter compile unchanged and keeps the defect, silently.