# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-structural-signals-indicate-that-mcp-http-native-transport-has-silently-degraded-to-sse-fallback-with-lost-session-resumption-state.md
# Remnant Warrant:    https://corpusfetch.com/c/what-structural-signals-indicate-that-mcp-http-native-transport-has-silently-degraded-to-sse-fallback-with-lost-session-resumption-state.warrant.json
# Markdown SHA-256:   4e56447c26b41c1509cc1a078f87257b8461761bf0543963882c7c30747b0697
# Code AST SHA-256:   f0d19779733d266280f7cd00f4cc8b94d7324c0cc4869b102c523f4be94e9eb1
# Verified Classes:   SilentFallbackResumptionError
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class SilentFallbackResumptionError(RuntimeError):
    """Raised when a session silently degraded to SSE fallback and lost resumption state."""
    pass


def classify_transport_transition(
    session_id_before: str,
    session_id_after: str,
    content_type_before: str,
    content_type_after: str,
    event_ids_after: list[int],
) -> str:
    if session_id_before != session_id_after:
        return "renegotiated_new_session"

    if content_type_before == "application/json" and content_type_after == "text/event-stream":
        if not event_ids_after or event_ids_after != sorted(event_ids_after):
            raise SilentFallbackResumptionError(
                f"session {session_id_after!r} downgraded to SSE fallback with "
                f"non-monotonic or absent event IDs; Last-Event-ID resumption is "
                f"undefined. Force renegotiation instead of resuming."
            )
        return "fallback_with_intact_resumption"

    return "native_unchanged"


def resume_or_renegotiate(transition: str, last_event_id: int | None) -> str:
    if transition == "fallback_with_intact_resumption":
        if last_event_id is None:
            raise SilentFallbackResumptionError(
                "fallback stream claims resumable IDs but client holds no cursor; "
                "refusing to resume without a defined Last-Event-ID."
            )
        return f"resume_from:{last_event_id}"
    if transition == "renegotiated_new_session":
        return "negotiate_fresh"
    return "continue_native"
