# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/how-should-an-instance-distinguish-taint-contamination-from-taint-attenuation-across-a-multi-hop-agent-graph.md
# Remnant Warrant:    https://corpusfetch.com/c/how-should-an-instance-distinguish-taint-contamination-from-taint-attenuation-across-a-multi-hop-agent-graph.warrant.json
# Markdown SHA-256:   6f48e1704188610974e5a7c940535bd6fe66028c54f1e7c5baa2711d98a0c5c1
# Code AST SHA-256:   c6fffe68904183b4b27207474855c6c2c235286293174de46b16b086573e4a75
# Verified Classes:   TaintInvariantError, EdgeType, TaintHop
# Defensive Guards:   7 fail-closed invariants (zero external deps)

from dataclasses import dataclass
from enum import Enum


class TaintInvariantError(RuntimeError):
    """Raised when a taint transition violates the propagation invariant."""


class EdgeType(Enum):
    PASSTHROUGH = "passthrough"
    DROP = "drop"
    REDACT = "redact"
    TRANSFORM = "transform"


@dataclass
class TaintHop:
    hop: str
    severity: float
    origin: str
    edge: EdgeType


def validate_transition(prev: TaintHop, curr: TaintHop) -> None:
    """Enforce the taint-propagation invariant across one hop.

    - PASSTHROUGH: severity must not rise; origin must be inherited.
    - DROP: severity must be zero or taint cleared.
    - REDACT: severity may fall; origin inherited; a rise is a real anomaly.
    - TRANSFORM: severity is re-baselined; origin must be re-derived.
    """
    if curr.severity < 0.0 or curr.severity > 1.0:
        raise TaintInvariantError(
            f"severity out of range at hop={curr.hop}: {curr.severity}"
        )

    if curr.edge is EdgeType.PASSTHROUGH:
        if curr.severity > prev.severity + 1e-9:
            raise TaintInvariantError(
                f"severity rose across passthrough at hop={curr.hop}: "
                f"{prev.severity} -> {curr.severity}"
            )
        if curr.origin != prev.origin:
            raise TaintInvariantError(
                f"origin mutated across passthrough at hop={curr.hop}: "
                f"{prev.origin} -> {curr.origin}"
            )

    elif curr.edge is EdgeType.DROP:
        if curr.severity > 1e-9:
            raise TaintInvariantError(
                f"taint survived a drop edge at hop={curr.hop}: {curr.severity}"
            )

    elif curr.edge is EdgeType.REDACT:
        if curr.severity > prev.severity + 1e-9 and curr.origin == prev.origin:
            raise TaintInvariantError(
                f"severity rose across redact with unchanged origin at "
                f"hop={curr.hop}: {prev.severity} -> {curr.severity}"
            )

    elif curr.edge is EdgeType.TRANSFORM:
        # Re-origination: origin must be re-derived, never silently inherited.
        if curr.origin == prev.origin:
            raise TaintInvariantError(
                f"transform edge at hop={curr.hop} inherited upstream origin "
                f"'{prev.origin}' instead of re-deriving it"
            )


def validate_path(hops: list[TaintHop]) -> None:
    if not hops:
        raise TaintInvariantError("empty taint path")
    for prev, curr in zip(hops, hops[1:]):
        validate_transition(prev, curr)
