"""C7 ``ThermalState`` DTO (frozen at AZ-297 v1.0.0).

Originally introduced as a forward-declared stub for AZ-355 (C4 needs
the type before C7 lands); AZ-297 expanded the surface to the full
contract shape (cpu/gpu temp, throttle, measured clock, monotonic
timestamp, telemetry-availability bit) and AZ-302 will own the
producer-side polling thread.

C4 consumers read ``thermal_throttle_active`` (the canonical name);
the older docstring references to ``throttle`` are stale and now
point at this field. Invariant I-6 of
``_docs/02_document/contracts/c7_inference/inference_runtime_protocol.md``
binds ``thermal_throttle_active == False`` whenever
``is_telemetry_available == False``.
"""

from __future__ import annotations

from dataclasses import dataclass

__all__ = ["ThermalState"]


@dataclass(frozen=True, slots=True)
class ThermalState:
    """C7-reported thermal state consumed by C4 for the per-frame mode switch.

    ``thermal_throttle_active == True`` triggers the Jacobian path
    (D-CROSS-LATENCY-1 / ADR-006); ``False`` keeps the production
    Marginals path. ``is_telemetry_available`` is the default-safe
    bit: when the source is hung or absent, consumers see
    ``thermal_throttle_active == False`` regardless of any other
    field's value.

    Temperatures are degrees Celsius; ``measured_clock_mhz`` is the
    most-recent GPU clock report (None when unavailable);
    ``measured_at_ns`` is :func:`time.monotonic_ns` at poll time so
    consumers can age-gate the reading.
    """

    cpu_temp_c: float | None
    gpu_temp_c: float | None
    thermal_throttle_active: bool
    measured_clock_mhz: int | None
    measured_at_ns: int
    is_telemetry_available: bool
