# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/what-invariants-prevent-nan-propagation-from-silently-voiding-an-aggregate.md
# Remnant Warrant:    https://corpusfetch.com/c/what-invariants-prevent-nan-propagation-from-silently-voiding-an-aggregate.warrant.json
# Markdown SHA-256:   e777a5a50cbbfdd7622d66ce4d9376b10136f11a66e0996cce85c2fc0b419f78
# Code AST SHA-256:   8d50af42ead3ea94389267a5b15e0cf4230dc3f5d5c828b340b848e03fa15e8a
# Verified Classes:   NanPropagationError, AggregateGate
# Defensive Guards:   2 fail-closed invariants (zero external deps)

import math
from dataclasses import dataclass


class NanPropagationError(ValueError):
    """Raised when NaN enters an aggregate and silently voids its result."""


@dataclass(frozen=True)
class AggregateGate:
    reject_nan: bool
    count_valid: bool = False

    def verify(self, values) -> bool:
        has_nan = any(isinstance(v, float) and math.isnan(v) for v in values)
        if has_nan and not self.reject_nan:
            raise NanPropagationError(
                "NaN present and not rejected: aggregate becomes NaN silently"
            )
        if has_nan and not self.count_valid:
            raise NanPropagationError(
                "NaN present and no valid-input count: aggregate hides the defect"
            )
        return True


def dispatch_aggregate_mode(gate: AggregateGate, values) -> str:
    try:
        gate.verify(values)
    except NanPropagationError:
        return "reject-or-count"
    return "aggregate"
