# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/where-must-an-instance-persist-the-last-event-id-cursor-session-resumption-vs-event-replay-in-mcp-http-native-streaming.md
# Remnant Warrant:    https://corpusfetch.com/c/where-must-an-instance-persist-the-last-event-id-cursor-session-resumption-vs-event-replay-in-mcp-http-native-streaming.warrant.json
# Markdown SHA-256:   582a491330287aaa1c440166fd710dd122607b54830b333a1732d7c4cd86bc37
# Code AST SHA-256:   882e3bed227a2de7e032ebb9a132ea81defd91f74dfb983c5ea482025f4cdbc9
# Verified Classes:   ResumptionGapError
# Defensive Guards:   3 fail-closed invariants (zero external deps)

class ResumptionGapError(RuntimeError):
    """Raised when a resumption request cannot be satisfied without an event gap."""
    pass


def resume_stream(session_id: str, last_event_id: str,
                  session_store, replay_buffer, buffer_head_id: str) -> list:
    # Session identity and event cursor are orthogonal; check both independently.
    if not session_store.exists(session_id):
        raise ResumptionGapError(
            f"session {session_id!r} unknown; cannot resume, client must re-init"
        )

    # The replay buffer is node-local. If this instance never served the stream,
    # it cannot replay the cursor even though the session exists.
    if last_event_id not in replay_buffer:
        raise ResumptionGapError(
            f"session {session_id!r} exists but Last-Event-ID {last_event_id!r} "
            f"is not in this instance's replay buffer (head={buffer_head_id!r}); "
            f"refusing to silently restart at head — signal gap to client"
        )

    # Only now is a contiguous replay actually guaranteed.
    return replay_buffer.events_after(last_event_id)


def handle_reconnect(session_id: str, last_event_id: str,
                     session_store, replay_buffer, buffer_head_id: str,
                     emit_discontinuity_signal) -> list:
    try:
        return resume_stream(session_id, last_event_id,
                             session_store, replay_buffer, buffer_head_id)
    except ResumptionGapError as exc:
        # Do not open a fresh stream at head and call it success.
        # Emit an explicit discontinuity so the client reconciles out-of-band.
        emit_discontinuity_signal(session_id, reason=str(exc))
        raise
